From 5cbf11db9228afc88f4d6357538b6de15d31ce23 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 25 Apr 2023 11:55:13 -0700 Subject: [PATCH 0001/1030] build: bump soname to 6 We are doing a lot of upcoming changes, so just synchronize everything to our next soname bump. --- meson.build | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/meson.build b/meson.build index 7a8edf33..77a0189e 100644 --- a/meson.build +++ b/meson.build @@ -20,8 +20,13 @@ else app_id = 'org.gnome.Sysprof' endif -libsysprof_api_version = 4 -libsysprof_ui_api_version = 5 +# All libraries share an ABI revision as they are expected +# to be updated as a set. +soname_major_version = 6 + +# Temporary until we update meson.build +libsysprof_api_version = soname_major_version +libsysprof_ui_api_version = soname_major_version version_split = meson.project_version().split('.') datadir = get_option('datadir') From e888d0602e2c661deec224e0bfbd7c12114dfacb Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 25 Apr 2023 11:55:25 -0700 Subject: [PATCH 0002/1030] build: always define SYSPROF_COMPILATION --- meson.build | 1 + 1 file changed, 1 insertion(+) diff --git a/meson.build b/meson.build index 77a0189e..fafd208e 100644 --- a/meson.build +++ b/meson.build @@ -133,6 +133,7 @@ endif global_c_args = [ + '-DSYSPROF_COMPILATION', '-D_GNU_SOURCE', '-D_POSIX_C_SOURCE=200809L', '-DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_@0@_@1@'.format(glib_major, glib_minor), From efab0450063796d009f68dccab8134e295070282 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 25 Apr 2023 11:57:26 -0700 Subject: [PATCH 0003/1030] libsysprof-analyze: start on sysprof-analyze library The goal here is to break up libsysprof into a library for recording profiles (using libsysprof-capture) and a library for analyzing profiles (both used by the sysprof UI). --- src/libsysprof-analyze/meson.build | 45 +++++++++++++++++++ src/libsysprof-analyze/sysprof-analyze.h | 32 +++++++++++++ .../sysprof-capture-frame-object-private.h | 0 .../sysprof-capture-frame-object.c | 0 .../sysprof-capture-frame-object.h | 0 .../sysprof-capture-model.c | 0 .../sysprof-capture-model.h | 0 src/libsysprof-analyze/tests/meson.build | 31 +++++++++++++ .../tests/test-capture-model.c | 3 +- src/libsysprof/meson.build | 4 -- src/libsysprof/sysprof.h | 2 - src/meson.build | 2 + src/tests/meson.build | 5 --- 13 files changed, 111 insertions(+), 13 deletions(-) create mode 100644 src/libsysprof-analyze/meson.build create mode 100644 src/libsysprof-analyze/sysprof-analyze.h rename src/{libsysprof => libsysprof-analyze}/sysprof-capture-frame-object-private.h (100%) rename src/{libsysprof => libsysprof-analyze}/sysprof-capture-frame-object.c (100%) rename src/{libsysprof => libsysprof-analyze}/sysprof-capture-frame-object.h (100%) rename src/{libsysprof => libsysprof-analyze}/sysprof-capture-model.c (100%) rename src/{libsysprof => libsysprof-analyze}/sysprof-capture-model.h (100%) create mode 100644 src/libsysprof-analyze/tests/meson.build rename src/{ => libsysprof-analyze}/tests/test-capture-model.c (97%) diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build new file mode 100644 index 00000000..c63aced6 --- /dev/null +++ b/src/libsysprof-analyze/meson.build @@ -0,0 +1,45 @@ +libsysprof_analyze_public_sources = [ + 'sysprof-capture-model.c', + 'sysprof-capture-frame-object.c', +] + +libsysprof_analyze_public_headers = [ + 'sysprof-analyze.h', + 'sysprof-capture-model.h', + 'sysprof-capture-frame-object.h', +] + +libsysprof_analyze_deps = [ + dependency('gio-2.0', version: glib_req_version), + libsysprof_capture_deps, +] + +libsysprof_analyze = library('sysprof-analyze', + libsysprof_analyze_public_sources, + include_directories: [include_directories('.'), + ipc_include_dirs, + libsysprof_capture_include_dirs], + dependencies: libsysprof_analyze_deps, + gnu_symbol_visibility: 'hidden', + version: '@0@.0.0'.format(soname_major_version), + darwin_versions: '@0@.0'.format(soname_major_version), +) + +libsysprof_analyze_dep = declare_dependency( + link_with: libsysprof_analyze, + dependencies: libsysprof_analyze_deps, + include_directories: [include_directories('.'), libsysprof_capture_include_dirs], +) +meson.override_dependency('sysprof-analyze-@0@'.format(soname_major_version), libsysprof_analyze_dep) + +pkgconfig.generate(libsysprof_analyze, + subdirs: [sysprof_header_subdir], + description: 'A library for processing and analyzing Sysprof captures', + install_dir: join_paths(get_option('libdir'), 'pkgconfig'), + requires: ['gio-2.0'], + variables: ['datadir=' + datadir_for_pc_file], +) + +install_headers(libsysprof_analyze_public_headers, subdir: sysprof_header_subdir) + +subdir('tests') diff --git a/src/libsysprof-analyze/sysprof-analyze.h b/src/libsysprof-analyze/sysprof-analyze.h new file mode 100644 index 00000000..caaaccf5 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-analyze.h @@ -0,0 +1,32 @@ +/* sysprof-analyze.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +#define SYSPROF_ANALYZE_INSIDE +# include "sysprof-capture-frame-object.h" +# include "sysprof-capture-model.h" +#undef SYSPROF_ANALYZE_INSIDE + +G_END_DECLS diff --git a/src/libsysprof/sysprof-capture-frame-object-private.h b/src/libsysprof-analyze/sysprof-capture-frame-object-private.h similarity index 100% rename from src/libsysprof/sysprof-capture-frame-object-private.h rename to src/libsysprof-analyze/sysprof-capture-frame-object-private.h diff --git a/src/libsysprof/sysprof-capture-frame-object.c b/src/libsysprof-analyze/sysprof-capture-frame-object.c similarity index 100% rename from src/libsysprof/sysprof-capture-frame-object.c rename to src/libsysprof-analyze/sysprof-capture-frame-object.c diff --git a/src/libsysprof/sysprof-capture-frame-object.h b/src/libsysprof-analyze/sysprof-capture-frame-object.h similarity index 100% rename from src/libsysprof/sysprof-capture-frame-object.h rename to src/libsysprof-analyze/sysprof-capture-frame-object.h diff --git a/src/libsysprof/sysprof-capture-model.c b/src/libsysprof-analyze/sysprof-capture-model.c similarity index 100% rename from src/libsysprof/sysprof-capture-model.c rename to src/libsysprof-analyze/sysprof-capture-model.c diff --git a/src/libsysprof/sysprof-capture-model.h b/src/libsysprof-analyze/sysprof-capture-model.h similarity index 100% rename from src/libsysprof/sysprof-capture-model.h rename to src/libsysprof-analyze/sysprof-capture-model.h diff --git a/src/libsysprof-analyze/tests/meson.build b/src/libsysprof-analyze/tests/meson.build new file mode 100644 index 00000000..d54c296a --- /dev/null +++ b/src/libsysprof-analyze/tests/meson.build @@ -0,0 +1,31 @@ +libsysprof_analyze_test_env = [ + 'G_DEBUG=gc-friendly', + 'GSETTINGS_BACKEND=memory', + 'MALLOC_CHECK_=2', +] + +libsysprof_analyze_testsuite_c_args = [ + '-DG_LOG_DOMAIN="libdex"', + '-DG_ENABLE_DEBUG', + '-UG_DISABLE_ASSERT', + '-UG_DISABLE_CAST_CHECKS', +] + +libsysprof_analyze_testsuite = { + 'test-capture-model': {'skip': true}, +} + +libsysprof_analyze_testsuite_deps = [ + libsysprof_analyze_dep, + libsysprof_capture_dep, +] + +foreach test, params: libsysprof_analyze_testsuite + test_exe = executable(test, '@0@.c'.format(test), + c_args: libsysprof_analyze_testsuite_c_args, + dependencies: libsysprof_analyze_testsuite_deps, + ) + if not params.get('skip', false) + test(test, test_exe, env: libsysprof_analyze_test_env) + endif +endforeach diff --git a/src/tests/test-capture-model.c b/src/libsysprof-analyze/tests/test-capture-model.c similarity index 97% rename from src/tests/test-capture-model.c rename to src/libsysprof-analyze/tests/test-capture-model.c index aca52f0b..3e4b1976 100644 --- a/src/tests/test-capture-model.c +++ b/src/libsysprof-analyze/tests/test-capture-model.c @@ -1,7 +1,6 @@ #include #include - -#include +#include int main (int argc, diff --git a/src/libsysprof/meson.build b/src/libsysprof/meson.build index 42c5f0af..a730ef11 100644 --- a/src/libsysprof/meson.build +++ b/src/libsysprof/meson.build @@ -3,9 +3,7 @@ libsysprof_c_args = [ '-DSYSPROF_COMPILATION' ] libsysprof_public_sources = [ 'sysprof-battery-source.c', 'sysprof-callgraph-profile.c', - 'sysprof-capture-frame-object.c', 'sysprof-capture-gobject.c', - 'sysprof-capture-model.c', 'sysprof-capture-symbol-resolver.c', 'sysprof-control-source.c', 'sysprof-diskstat-source.c', @@ -38,9 +36,7 @@ libsysprof_public_headers = [ 'sysprof-battery-source.h', 'sysprof-callgraph-profile.h', 'sysprof-capture-autocleanups.h', - 'sysprof-capture-frame-object.h', 'sysprof-capture-gobject.h', - 'sysprof-capture-model.h', 'sysprof-capture-symbol-resolver.h', 'sysprof-control-source.h', 'sysprof-diskstat-source.h', diff --git a/src/libsysprof/sysprof.h b/src/libsysprof/sysprof.h index ac96871b..5abe64b6 100644 --- a/src/libsysprof/sysprof.h +++ b/src/libsysprof/sysprof.h @@ -28,9 +28,7 @@ G_BEGIN_DECLS # include "sysprof-battery-source.h" # include "sysprof-callgraph-profile.h" # include "sysprof-capture-autocleanups.h" -# include "sysprof-capture-frame-object.h" # include "sysprof-capture-gobject.h" -# include "sysprof-capture-model.h" # include "sysprof-capture-symbol-resolver.h" # include "sysprof-control-source.h" # include "sysprof-diskstat-source.h" diff --git a/src/meson.build b/src/meson.build index 9d8817eb..a74f2754 100644 --- a/src/meson.build +++ b/src/meson.build @@ -55,6 +55,8 @@ stackstash_sources = files('stackstash.c') helpers_sources = files('helpers.c') subdir('libsysprof-capture') +subdir('libsysprof-analyze') + if get_option('sysprofd') == 'bundled' subdir('sysprofd') endif diff --git a/src/tests/meson.build b/src/tests/meson.build index 2dbc717b..611ff260 100644 --- a/src/tests/meson.build +++ b/src/tests/meson.build @@ -56,11 +56,6 @@ if get_option('libsysprof') dependencies: test_deps, ) - test_capture_model = executable('test-capture-model', 'test-capture-model.c', - c_args: test_cflags, - dependencies: test_deps, - ) - test_mountinfo = executable('test-mountinfo', 'test-mountinfo.c', c_args: test_cflags, dependencies: test_deps, From a97f6a34b060e2174e78a5d35414acad73917985 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 25 Apr 2023 15:23:48 -0700 Subject: [PATCH 0004/1030] build: bump GLib and GTK requirements --- meson.build | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/meson.build b/meson.build index fafd208e..1db87099 100644 --- a/meson.build +++ b/meson.build @@ -33,8 +33,8 @@ datadir = get_option('datadir') datadir_for_pc_file = join_paths('${prefix}', datadir) podir = join_paths(meson.current_source_dir(), 'po') -glib_req = '2.73.0' -gtk_req = '4.6' +glib_req = '2.76.0' +gtk_req = '4.10' polkit_req = '0.105' glib_req_version = '>= @0@'.format(glib_req) From 63a9f498eaf06b3b2912dd73fdd01fd0a6fc4fd1 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 25 Apr 2023 15:24:59 -0700 Subject: [PATCH 0005/1030] gitignore: ignore .flatpak-builder directory --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 1682ae21..ed98732c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.flatpak-builder *.swp *~ build From ed01673a5e33b37efeace3ba883f656b0509dc80 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 25 Apr 2023 15:26:06 -0700 Subject: [PATCH 0006/1030] libsysprof-analyzer: rename model to SysprofDocument This will provide better namespacing for the objects inflated from the document for various frame types. By creating real objects with real properties we give ourselves quite a bit of flexibility in the data filtering language coming forth. --- src/libsysprof-analyze/meson.build | 4 +- src/libsysprof-analyze/sysprof-analyze.h | 2 +- ...rof-capture-model.c => sysprof-document.c} | 114 +++++++++++++----- ...rof-capture-model.h => sysprof-document.h} | 15 ++- .../tests/test-capture-model.c | 20 +-- 5 files changed, 104 insertions(+), 51 deletions(-) rename src/libsysprof-analyze/{sysprof-capture-model.c => sysprof-document.c} (54%) rename src/libsysprof-analyze/{sysprof-capture-model.h => sysprof-document.h} (63%) diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build index c63aced6..f35a7ade 100644 --- a/src/libsysprof-analyze/meson.build +++ b/src/libsysprof-analyze/meson.build @@ -1,11 +1,11 @@ libsysprof_analyze_public_sources = [ - 'sysprof-capture-model.c', + 'sysprof-document.c', 'sysprof-capture-frame-object.c', ] libsysprof_analyze_public_headers = [ 'sysprof-analyze.h', - 'sysprof-capture-model.h', + 'sysprof-document.h', 'sysprof-capture-frame-object.h', ] diff --git a/src/libsysprof-analyze/sysprof-analyze.h b/src/libsysprof-analyze/sysprof-analyze.h index caaaccf5..8b1dbc3e 100644 --- a/src/libsysprof-analyze/sysprof-analyze.h +++ b/src/libsysprof-analyze/sysprof-analyze.h @@ -26,7 +26,7 @@ G_BEGIN_DECLS #define SYSPROF_ANALYZE_INSIDE # include "sysprof-capture-frame-object.h" -# include "sysprof-capture-model.h" +# include "sysprof-document.h" #undef SYSPROF_ANALYZE_INSIDE G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-capture-model.c b/src/libsysprof-analyze/sysprof-document.c similarity index 54% rename from src/libsysprof-analyze/sysprof-capture-model.c rename to src/libsysprof-analyze/sysprof-document.c index b9ae28f0..d3842526 100644 --- a/src/libsysprof-analyze/sysprof-capture-model.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -1,4 +1,4 @@ -/* sysprof-capture-model.c +/* sysprof-document.c * * Copyright 2023 Christian Hergert * @@ -20,10 +20,14 @@ #include "config.h" -#include "sysprof-capture-frame-object-private.h" -#include "sysprof-capture-model.h" +#include -struct _SysprofCaptureModel +#include + +#include "sysprof-capture-frame-object-private.h" +#include "sysprof-document.h" + +struct _SysprofDocument { GObject parent_instance; GPtrArray *frames; @@ -33,22 +37,22 @@ struct _SysprofCaptureModel }; static GType -sysprof_capture_model_get_item_type (GListModel *model) +sysprof_document_get_item_type (GListModel *model) { return SYSPROF_TYPE_CAPTURE_FRAME_OBJECT; } static guint -sysprof_capture_model_get_n_items (GListModel *model) +sysprof_document_get_n_items (GListModel *model) { - return SYSPROF_CAPTURE_MODEL (model)->frames->len; + return SYSPROF_DOCUMENT (model)->frames->len; } static gpointer -sysprof_capture_model_get_item (GListModel *model, +sysprof_document_get_item (GListModel *model, guint position) { - SysprofCaptureModel *self = SYSPROF_CAPTURE_MODEL (model); + SysprofDocument *self = SYSPROF_DOCUMENT (model); if (position >= self->frames->len) return NULL; @@ -61,40 +65,40 @@ sysprof_capture_model_get_item (GListModel *model, static void list_model_iface_init (GListModelInterface *iface) { - iface->get_item_type = sysprof_capture_model_get_item_type; - iface->get_n_items = sysprof_capture_model_get_n_items; - iface->get_item = sysprof_capture_model_get_item; + iface->get_item_type = sysprof_document_get_item_type; + iface->get_n_items = sysprof_document_get_n_items; + iface->get_item = sysprof_document_get_item; } -G_DEFINE_FINAL_TYPE_WITH_CODE (SysprofCaptureModel, sysprof_capture_model, G_TYPE_OBJECT, +G_DEFINE_FINAL_TYPE_WITH_CODE (SysprofDocument, sysprof_document, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init)) static void -sysprof_capture_model_finalize (GObject *object) +sysprof_document_finalize (GObject *object) { - SysprofCaptureModel *self = (SysprofCaptureModel *)object; + SysprofDocument *self = (SysprofDocument *)object; g_clear_pointer (&self->mapped_file, g_mapped_file_unref); g_clear_pointer (&self->frames, g_ptr_array_unref); - G_OBJECT_CLASS (sysprof_capture_model_parent_class)->finalize (object); + G_OBJECT_CLASS (sysprof_document_parent_class)->finalize (object); } static void -sysprof_capture_model_class_init (SysprofCaptureModelClass *klass) +sysprof_document_class_init (SysprofDocumentClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); - object_class->finalize = sysprof_capture_model_finalize; + object_class->finalize = sysprof_document_finalize; } static void -sysprof_capture_model_init (SysprofCaptureModel *self) +sysprof_document_init (SysprofDocument *self) { self->frames = g_ptr_array_new (); } static gboolean -sysprof_capture_model_load (SysprofCaptureModel *self, +sysprof_document_load (SysprofDocument *self, int capture_fd, GError **error) { @@ -102,7 +106,7 @@ sysprof_capture_model_load (SysprofCaptureModel *self, goffset pos; gsize len; - g_assert (SYSPROF_IS_CAPTURE_MODEL (self)); + g_assert (SYSPROF_IS_DOCUMENT (self)); g_assert (capture_fd > -1); if (!(self->mapped_file = g_mapped_file_new_from_fd (capture_fd, FALSE, error))) @@ -148,17 +152,73 @@ sysprof_capture_model_load (SysprofCaptureModel *self, return TRUE; } -SysprofCaptureModel * -sysprof_capture_model_new_from_fd (int capture_fd, - GError **error) +/** + * sysprof_document_new_from_fd: + * @capture_fd: a file-descriptor to be mapped + * @error: a location for a #GError, or %NULL + * + * Creates a new memory map using @capture_fd to read the underlying + * Sysprof capture. + * + * No ownership of @capture_fd is transferred, and the caller may close + * @capture_fd after calling this function. + * + * Returns: A #SysprofDocument if successful; otherwise %NULL + * and @error is set. + * + * Since: 45.0 + */ +SysprofDocument * +sysprof_document_new_from_fd (int capture_fd, + GError **error) { - g_autoptr(SysprofCaptureModel) self = NULL; + g_autoptr(SysprofDocument) self = NULL; g_return_val_if_fail (capture_fd > -1, NULL); - self = g_object_new (SYSPROF_TYPE_CAPTURE_MODEL, NULL); + self = g_object_new (SYSPROF_TYPE_DOCUMENT, NULL); - if (!sysprof_capture_model_load (self, capture_fd, error)) + if (!sysprof_document_load (self, capture_fd, error)) + return NULL; + + return g_steal_pointer (&self); +} + +/** + * sysprof_document_new: + * @filename: a path to a capture file + * @error: location for a #GError, or %NULL + * + * Similar to sysprof_document_new_from_fd() but opens the file found + * at @filename as a #GMappedFile. + * + * Returns: a #SysprofDocument if successful; otherwise %NULL + * and @error is set. + * + * Since: 45.0 + */ +SysprofDocument * +sysprof_document_new (const char *filename, + GError **error) +{ + g_autoptr(SysprofDocument) self = NULL; + g_autofd int capture_fd = -1; + + g_return_val_if_fail (filename != NULL, NULL); + + if (-1 == (capture_fd = g_open (filename, O_RDONLY|O_CLOEXEC, 0))) + { + int errsv = errno; + g_set_error (error, + G_FILE_ERROR, + g_file_error_from_errno (errsv), + "%s", g_strerror (errsv)); + return NULL; + } + + self = g_object_new (SYSPROF_TYPE_DOCUMENT, NULL); + + if (!sysprof_document_load (self, capture_fd, error)) return NULL; return g_steal_pointer (&self); diff --git a/src/libsysprof-analyze/sysprof-capture-model.h b/src/libsysprof-analyze/sysprof-document.h similarity index 63% rename from src/libsysprof-analyze/sysprof-capture-model.h rename to src/libsysprof-analyze/sysprof-document.h index 71c2e227..4d76f228 100644 --- a/src/libsysprof-analyze/sysprof-capture-model.h +++ b/src/libsysprof-analyze/sysprof-document.h @@ -1,4 +1,4 @@ -/* sysprof-capture-model.h +/* sysprof-document.h * * Copyright 2023 Christian Hergert * @@ -22,17 +22,20 @@ #include -#include +#include "sysprof-version-macros.h" G_BEGIN_DECLS -#define SYSPROF_TYPE_CAPTURE_MODEL (sysprof_capture_model_get_type()) +#define SYSPROF_TYPE_DOCUMENT (sysprof_document_get_type()) SYSPROF_AVAILABLE_IN_ALL -G_DECLARE_FINAL_TYPE (SysprofCaptureModel, sysprof_capture_model, SYSPROF, CAPTURE_MODEL, GObject) +G_DECLARE_FINAL_TYPE (SysprofDocument, sysprof_document, SYSPROF, DOCUMENT, GObject) SYSPROF_AVAILABLE_IN_ALL -SysprofCaptureModel *sysprof_capture_model_new_from_fd (int capture_fd, - GError **error); +SysprofDocument *sysprof_document_new (const char *filename, + GError **error); +SYSPROF_AVAILABLE_IN_ALL +SysprofDocument *sysprof_document_new_from_fd (int capture_fd, + GError **error); G_END_DECLS diff --git a/src/libsysprof-analyze/tests/test-capture-model.c b/src/libsysprof-analyze/tests/test-capture-model.c index 3e4b1976..f240434b 100644 --- a/src/libsysprof-analyze/tests/test-capture-model.c +++ b/src/libsysprof-analyze/tests/test-capture-model.c @@ -6,11 +6,10 @@ int main (int argc, char *argv[]) { - SysprofCaptureModel *model; + SysprofDocument *document; const char *filename; GError *error = NULL; guint n_items; - int fd; sysprof_clock_init (); @@ -21,35 +20,26 @@ main (int argc, } filename = argv[1]; - fd = open (filename, O_RDONLY|O_CLOEXEC); - if (fd == -1) - { - g_printerr ("Failed to open %s: %s\n", - filename, g_strerror (errno)); - return 1; - } - - if (!(model = sysprof_capture_model_new_from_fd (fd, &error))) + if (!(document = sysprof_document_new (filename, &error))) { g_printerr ("Failed to load %s: %s\n", filename, error->message); return 1; } - n_items = g_list_model_get_n_items (G_LIST_MODEL (model)); + n_items = g_list_model_get_n_items (G_LIST_MODEL (document)); g_print ("%u frames\n", n_items); for (guint i = 0; i < n_items; i++) { - SysprofCaptureFrameObject *obj = g_list_model_get_item (G_LIST_MODEL (model), i); + SysprofCaptureFrameObject *obj = g_list_model_get_item (G_LIST_MODEL (document), i); g_clear_object (&obj); } - close (fd); - g_clear_object (&model); + g_clear_object (&document); return 0; } From 31003c519cf7f2219459924d95ad3ba675e97f6a Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 25 Apr 2023 15:32:55 -0700 Subject: [PATCH 0007/1030] libsysprof-analyze: rename to SysprofDocumentFrame We will eventually be adding sub-types for the various frame types, and use this as a common ancestor for item inflation. --- src/libsysprof-analyze/meson.build | 4 +-- src/libsysprof-analyze/sysprof-analyze.h | 2 +- ...ate.h => sysprof-document-frame-private.h} | 14 +++++--- ...rame-object.c => sysprof-document-frame.c} | 32 +++++++++---------- ...rame-object.h => sysprof-document-frame.h} | 14 +++----- src/libsysprof-analyze/sysprof-document.c | 10 +++--- .../tests/test-capture-model.c | 6 ++-- 7 files changed, 42 insertions(+), 40 deletions(-) rename src/libsysprof-analyze/{sysprof-capture-frame-object-private.h => sysprof-document-frame-private.h} (70%) rename src/libsysprof-analyze/{sysprof-capture-frame-object.c => sysprof-document-frame.c} (56%) rename src/libsysprof-analyze/{sysprof-capture-frame-object.h => sysprof-document-frame.h} (67%) diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build index f35a7ade..d803f25a 100644 --- a/src/libsysprof-analyze/meson.build +++ b/src/libsysprof-analyze/meson.build @@ -1,12 +1,12 @@ libsysprof_analyze_public_sources = [ 'sysprof-document.c', - 'sysprof-capture-frame-object.c', + 'sysprof-document-frame.c', ] libsysprof_analyze_public_headers = [ 'sysprof-analyze.h', 'sysprof-document.h', - 'sysprof-capture-frame-object.h', + 'sysprof-document-frame.h', ] libsysprof_analyze_deps = [ diff --git a/src/libsysprof-analyze/sysprof-analyze.h b/src/libsysprof-analyze/sysprof-analyze.h index 8b1dbc3e..9f6a9860 100644 --- a/src/libsysprof-analyze/sysprof-analyze.h +++ b/src/libsysprof-analyze/sysprof-analyze.h @@ -25,8 +25,8 @@ G_BEGIN_DECLS #define SYSPROF_ANALYZE_INSIDE -# include "sysprof-capture-frame-object.h" # include "sysprof-document.h" +# include "sysprof-document-frame.h" #undef SYSPROF_ANALYZE_INSIDE G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-capture-frame-object-private.h b/src/libsysprof-analyze/sysprof-document-frame-private.h similarity index 70% rename from src/libsysprof-analyze/sysprof-capture-frame-object-private.h rename to src/libsysprof-analyze/sysprof-document-frame-private.h index 34d6ce2e..74f493b5 100644 --- a/src/libsysprof-analyze/sysprof-capture-frame-object-private.h +++ b/src/libsysprof-analyze/sysprof-document-frame-private.h @@ -1,4 +1,4 @@ -/* sysprof-capture-frame-object-private.h +/* sysprof-document-frame-private.h * * Copyright 2023 Christian Hergert * @@ -18,12 +18,16 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -#include "sysprof-capture-frame-object.h" +#pragma once + +#include + +#include "sysprof-document-frame.h" G_BEGIN_DECLS -SysprofCaptureFrameObject *sysprof_capture_frame_object_new (GMappedFile *mapped, - gconstpointer data, - gboolean is_native); +SysprofDocumentFrame *sysprof_document_frame_new (GMappedFile *mapped, + gconstpointer data, + gboolean is_native); G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-capture-frame-object.c b/src/libsysprof-analyze/sysprof-document-frame.c similarity index 56% rename from src/libsysprof-analyze/sysprof-capture-frame-object.c rename to src/libsysprof-analyze/sysprof-document-frame.c index 1cf6e983..1dc6ff89 100644 --- a/src/libsysprof-analyze/sysprof-capture-frame-object.c +++ b/src/libsysprof-analyze/sysprof-document-frame.c @@ -1,4 +1,4 @@ -/* sysprof-capture-frame-object.c +/* sysprof-document-frame.c * * Copyright 2023 Christian Hergert * @@ -20,9 +20,9 @@ #include "config.h" -#include "sysprof-capture-frame-object-private.h" +#include "sysprof-document-frame-private.h" -struct _SysprofCaptureFrameObject +struct _SysprofDocumentFrame { GObject parent_instance; GMappedFile *mapped_file; @@ -30,40 +30,40 @@ struct _SysprofCaptureFrameObject guint is_native : 1; }; -G_DEFINE_FINAL_TYPE (SysprofCaptureFrameObject, sysprof_capture_frame_object, G_TYPE_OBJECT) +G_DEFINE_FINAL_TYPE (SysprofDocumentFrame, sysprof_document_frame, G_TYPE_OBJECT) static void -sysprof_capture_frame_object_finalize (GObject *object) +sysprof_document_frame_finalize (GObject *object) { - SysprofCaptureFrameObject *self = (SysprofCaptureFrameObject *)object; + SysprofDocumentFrame *self = (SysprofDocumentFrame *)object; g_clear_pointer (&self->mapped_file, g_mapped_file_unref); self->frame = NULL; - G_OBJECT_CLASS (sysprof_capture_frame_object_parent_class)->finalize (object); + G_OBJECT_CLASS (sysprof_document_frame_parent_class)->finalize (object); } static void -sysprof_capture_frame_object_class_init (SysprofCaptureFrameObjectClass *klass) +sysprof_document_frame_class_init (SysprofDocumentFrameClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); - object_class->finalize = sysprof_capture_frame_object_finalize; + object_class->finalize = sysprof_document_frame_finalize; } static void -sysprof_capture_frame_object_init (SysprofCaptureFrameObject *self) +sysprof_document_frame_init (SysprofDocumentFrame *self) { } -SysprofCaptureFrameObject * -sysprof_capture_frame_object_new (GMappedFile *mapped_file, - gconstpointer data, - gboolean is_native) +SysprofDocumentFrame * +sysprof_document_frame_new (GMappedFile *mapped_file, + gconstpointer data, + gboolean is_native) { - SysprofCaptureFrameObject *self; + SysprofDocumentFrame *self; - self = g_object_new (SYSPROF_TYPE_CAPTURE_FRAME_OBJECT, NULL); + self = g_object_new (SYSPROF_TYPE_DOCUMENT_FRAME, NULL); self->mapped_file = g_mapped_file_ref (mapped_file); self->frame = data; self->is_native = !!is_native; diff --git a/src/libsysprof-analyze/sysprof-capture-frame-object.h b/src/libsysprof-analyze/sysprof-document-frame.h similarity index 67% rename from src/libsysprof-analyze/sysprof-capture-frame-object.h rename to src/libsysprof-analyze/sysprof-document-frame.h index ee64063e..eaca9d51 100644 --- a/src/libsysprof-analyze/sysprof-capture-frame-object.h +++ b/src/libsysprof-analyze/sysprof-document-frame.h @@ -1,4 +1,4 @@ -/* sysprof-capture-frame-object.h +/* sysprof-document-frame.h * * Copyright 2023 Christian Hergert * @@ -20,18 +20,14 @@ #pragma once -#include - -#include +#include G_BEGIN_DECLS -#define SYSPROF_TYPE_CAPTURE_FRAME_OBJECT (sysprof_capture_frame_object_get_type()) +#define SYSPROF_TYPE_DOCUMENT_FRAME (sysprof_document_frame_get_type()) SYSPROF_AVAILABLE_IN_ALL -G_DECLARE_FINAL_TYPE (SysprofCaptureFrameObject, sysprof_capture_frame_object, SYSPROF, CAPTURE_FRAME_OBJECT, GObject) - -SYSPROF_AVAILABLE_IN_ALL -SysprofCaptureFrame *sysprof_capture_frame_object_get_frame (SysprofCaptureFrameObject *self); +G_DECLARE_FINAL_TYPE (SysprofDocumentFrame, sysprof_document_frame, SYSPROF, CAPTURE_FRAME_OBJECT, GObject) G_END_DECLS + diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index d3842526..588d9b75 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -24,8 +24,8 @@ #include -#include "sysprof-capture-frame-object-private.h" #include "sysprof-document.h" +#include "sysprof-document-frame-private.h" struct _SysprofDocument { @@ -39,7 +39,7 @@ struct _SysprofDocument static GType sysprof_document_get_item_type (GListModel *model) { - return SYSPROF_TYPE_CAPTURE_FRAME_OBJECT; + return SYSPROF_TYPE_DOCUMENT_FRAME; } static guint @@ -57,9 +57,9 @@ sysprof_document_get_item (GListModel *model, if (position >= self->frames->len) return NULL; - return sysprof_capture_frame_object_new (self->mapped_file, - g_ptr_array_index (self->frames, position), - self->is_native); + return sysprof_document_frame_new (self->mapped_file, + g_ptr_array_index (self->frames, position), + self->is_native); } static void diff --git a/src/libsysprof-analyze/tests/test-capture-model.c b/src/libsysprof-analyze/tests/test-capture-model.c index f240434b..ef144d96 100644 --- a/src/libsysprof-analyze/tests/test-capture-model.c +++ b/src/libsysprof-analyze/tests/test-capture-model.c @@ -1,6 +1,8 @@ #include #include + #include +#include int main (int argc, @@ -34,9 +36,9 @@ main (int argc, for (guint i = 0; i < n_items; i++) { - SysprofCaptureFrameObject *obj = g_list_model_get_item (G_LIST_MODEL (document), i); + SysprofDocumentFrame *frame = g_list_model_get_item (G_LIST_MODEL (document), i); - g_clear_object (&obj); + g_clear_object (&frame); } g_clear_object (&document); From 69930dc8766f1a531f1b41a926f47d30f40e0653 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 25 Apr 2023 15:55:41 -0700 Subject: [PATCH 0008/1030] libsysprof-analyze: read property access to frame header Also handle swapping the data when non-native endian so that we can leave our GMappedFile unperturbed (and therefore not have to keep those bytes as dirty pages in process). --- .../sysprof-document-frame-private.h | 5 + .../sysprof-document-frame.c | 130 ++++++++++++++++-- .../sysprof-document-frame.h | 10 +- 3 files changed, 135 insertions(+), 10 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-document-frame-private.h b/src/libsysprof-analyze/sysprof-document-frame-private.h index 74f493b5..c3d43834 100644 --- a/src/libsysprof-analyze/sysprof-document-frame-private.h +++ b/src/libsysprof-analyze/sysprof-document-frame-private.h @@ -26,6 +26,11 @@ G_BEGIN_DECLS +struct _SysprofDocumentFrameClass +{ + GObjectClass parent_class; +}; + SysprofDocumentFrame *sysprof_document_frame_new (GMappedFile *mapped, gconstpointer data, gboolean is_native); diff --git a/src/libsysprof-analyze/sysprof-document-frame.c b/src/libsysprof-analyze/sysprof-document-frame.c index 1dc6ff89..6b539b60 100644 --- a/src/libsysprof-analyze/sysprof-document-frame.c +++ b/src/libsysprof-analyze/sysprof-document-frame.c @@ -22,33 +22,96 @@ #include "sysprof-document-frame-private.h" -struct _SysprofDocumentFrame +typedef struct _SysprofDocumentFramePrivate { - GObject parent_instance; GMappedFile *mapped_file; const SysprofCaptureFrame *frame; guint is_native : 1; +} SysprofDocumentFramePrivate; + +G_DEFINE_TYPE_WITH_PRIVATE (SysprofDocumentFrame, sysprof_document_frame, G_TYPE_OBJECT) + +enum { + PROP_0, + PROP_CPU, + PROP_PID, + PROP_TIME, + N_PROPS }; -G_DEFINE_FINAL_TYPE (SysprofDocumentFrame, sysprof_document_frame, G_TYPE_OBJECT) +static GParamSpec *properties[N_PROPS]; static void sysprof_document_frame_finalize (GObject *object) { SysprofDocumentFrame *self = (SysprofDocumentFrame *)object; + SysprofDocumentFramePrivate *priv = sysprof_document_frame_get_instance_private (self); - g_clear_pointer (&self->mapped_file, g_mapped_file_unref); - self->frame = NULL; + g_clear_pointer (&priv->mapped_file, g_mapped_file_unref); + + priv->frame = NULL; + priv->is_native = 0; G_OBJECT_CLASS (sysprof_document_frame_parent_class)->finalize (object); } +static void +sysprof_document_frame_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofDocumentFrame *self = SYSPROF_DOCUMENT_FRAME (object); + + switch (prop_id) + { + case PROP_CPU: + g_value_set_int (value, sysprof_document_frame_get_cpu (self)); + break; + + case PROP_PID: + g_value_set_int (value, sysprof_document_frame_get_pid (self)); + break; + + case PROP_TIME: + g_value_set_int64 (value, sysprof_document_frame_get_time (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + static void sysprof_document_frame_class_init (SysprofDocumentFrameClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = sysprof_document_frame_finalize; + object_class->get_property = sysprof_document_frame_get_property; + + properties[PROP_CPU] = + g_param_spec_int ("cpu", NULL, NULL, + G_MININT32, + G_MAXINT32, + -1, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + properties[PROP_PID] = + g_param_spec_int ("pid", NULL, NULL, + G_MININT32, + G_MAXINT32, + -1, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + properties[PROP_TIME] = + g_param_spec_int64 ("time", NULL, NULL, + G_MININT64, + G_MAXINT64, + 0, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); } static void @@ -62,11 +125,62 @@ sysprof_document_frame_new (GMappedFile *mapped_file, gboolean is_native) { SysprofDocumentFrame *self; + SysprofDocumentFramePrivate *priv; self = g_object_new (SYSPROF_TYPE_DOCUMENT_FRAME, NULL); - self->mapped_file = g_mapped_file_ref (mapped_file); - self->frame = data; - self->is_native = !!is_native; + + priv = sysprof_document_frame_get_instance_private (self); + priv->mapped_file = g_mapped_file_ref (mapped_file); + priv->frame = data; + priv->is_native = !!is_native; return self; } + +int +sysprof_document_frame_get_cpu (SysprofDocumentFrame *self) +{ + SysprofDocumentFramePrivate *priv = sysprof_document_frame_get_instance_private (self); + int ret; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_FRAME (self), 0); + + if G_LIKELY (priv->is_native) + ret = priv->frame->cpu; + else + ret = GUINT32_SWAP_LE_BE (priv->frame->cpu); + + return ret; +} + +int +sysprof_document_frame_get_pid (SysprofDocumentFrame *self) +{ + SysprofDocumentFramePrivate *priv = sysprof_document_frame_get_instance_private (self); + int ret; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_FRAME (self), 0); + + if G_LIKELY (priv->is_native) + ret = priv->frame->pid; + else + ret = GUINT32_SWAP_LE_BE (priv->frame->pid); + + return ret; +} + +gint64 +sysprof_document_frame_get_time (SysprofDocumentFrame *self) +{ + SysprofDocumentFramePrivate *priv = sysprof_document_frame_get_instance_private (self); + gint64 ret; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_FRAME (self), 0); + + if G_LIKELY (priv->is_native) + ret = priv->frame->time; + else + ret = GUINT32_SWAP_LE_BE (priv->frame->time); + + return ret; +} diff --git a/src/libsysprof-analyze/sysprof-document-frame.h b/src/libsysprof-analyze/sysprof-document-frame.h index eaca9d51..c8e81b6e 100644 --- a/src/libsysprof-analyze/sysprof-document-frame.h +++ b/src/libsysprof-analyze/sysprof-document-frame.h @@ -27,7 +27,13 @@ G_BEGIN_DECLS #define SYSPROF_TYPE_DOCUMENT_FRAME (sysprof_document_frame_get_type()) SYSPROF_AVAILABLE_IN_ALL -G_DECLARE_FINAL_TYPE (SysprofDocumentFrame, sysprof_document_frame, SYSPROF, CAPTURE_FRAME_OBJECT, GObject) +G_DECLARE_DERIVABLE_TYPE (SysprofDocumentFrame, sysprof_document_frame, SYSPROF, DOCUMENT_FRAME, GObject) + +SYSPROF_AVAILABLE_IN_ALL +int sysprof_document_frame_get_cpu (SysprofDocumentFrame *self); +SYSPROF_AVAILABLE_IN_ALL +int sysprof_document_frame_get_pid (SysprofDocumentFrame *self); +SYSPROF_AVAILABLE_IN_ALL +gint64 sysprof_document_frame_get_time (SysprofDocumentFrame *self); G_END_DECLS - From a3cf041326be7c311c82cefcca5f6846ca673fe5 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 25 Apr 2023 16:04:35 -0700 Subject: [PATCH 0009/1030] libsysprof-analyze: print some basic frame information --- src/libsysprof-analyze/tests/test-capture-model.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/libsysprof-analyze/tests/test-capture-model.c b/src/libsysprof-analyze/tests/test-capture-model.c index ef144d96..dba4918b 100644 --- a/src/libsysprof-analyze/tests/test-capture-model.c +++ b/src/libsysprof-analyze/tests/test-capture-model.c @@ -32,15 +32,20 @@ main (int argc, n_items = g_list_model_get_n_items (G_LIST_MODEL (document)); - g_print ("%u frames\n", n_items); - for (guint i = 0; i < n_items; i++) { SysprofDocumentFrame *frame = g_list_model_get_item (G_LIST_MODEL (document), i); + g_print ("%"G_GINT64_FORMAT" [pid %d] [cpu %d]\n", + sysprof_document_frame_get_time (frame), + sysprof_document_frame_get_pid (frame), + sysprof_document_frame_get_cpu (frame)); + g_clear_object (&frame); } + g_printerr ("%u frames\n", n_items); + g_clear_object (&document); return 0; From a5dafa5409ed34426422eff531ec9ae334de0933 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 25 Apr 2023 16:11:59 -0700 Subject: [PATCH 0010/1030] libsysprof-analyze: rename some private API prefix with _, use frame type directly, use needs_swap rather than is_native so it's more clear to readers. --- .../sysprof-document-frame-private.h | 6 +++--- .../sysprof-document-frame.c | 20 +++++++++---------- src/libsysprof-analyze/sysprof-document.c | 18 ++++++++--------- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-document-frame-private.h b/src/libsysprof-analyze/sysprof-document-frame-private.h index c3d43834..9e0ea4c5 100644 --- a/src/libsysprof-analyze/sysprof-document-frame-private.h +++ b/src/libsysprof-analyze/sysprof-document-frame-private.h @@ -31,8 +31,8 @@ struct _SysprofDocumentFrameClass GObjectClass parent_class; }; -SysprofDocumentFrame *sysprof_document_frame_new (GMappedFile *mapped, - gconstpointer data, - gboolean is_native); +SysprofDocumentFrame *_sysprof_document_frame_new (GMappedFile *mapped, + const SysprofCaptureFrame *frame, + gboolean needs_swap); G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-document-frame.c b/src/libsysprof-analyze/sysprof-document-frame.c index 6b539b60..e3e8bad5 100644 --- a/src/libsysprof-analyze/sysprof-document-frame.c +++ b/src/libsysprof-analyze/sysprof-document-frame.c @@ -26,7 +26,7 @@ typedef struct _SysprofDocumentFramePrivate { GMappedFile *mapped_file; const SysprofCaptureFrame *frame; - guint is_native : 1; + guint needs_swap : 1; } SysprofDocumentFramePrivate; G_DEFINE_TYPE_WITH_PRIVATE (SysprofDocumentFrame, sysprof_document_frame, G_TYPE_OBJECT) @@ -50,7 +50,7 @@ sysprof_document_frame_finalize (GObject *object) g_clear_pointer (&priv->mapped_file, g_mapped_file_unref); priv->frame = NULL; - priv->is_native = 0; + priv->needs_swap = 0; G_OBJECT_CLASS (sysprof_document_frame_parent_class)->finalize (object); } @@ -120,9 +120,9 @@ sysprof_document_frame_init (SysprofDocumentFrame *self) } SysprofDocumentFrame * -sysprof_document_frame_new (GMappedFile *mapped_file, - gconstpointer data, - gboolean is_native) +_sysprof_document_frame_new (GMappedFile *mapped_file, + const SysprofCaptureFrame *frame, + gboolean needs_swap) { SysprofDocumentFrame *self; SysprofDocumentFramePrivate *priv; @@ -131,8 +131,8 @@ sysprof_document_frame_new (GMappedFile *mapped_file, priv = sysprof_document_frame_get_instance_private (self); priv->mapped_file = g_mapped_file_ref (mapped_file); - priv->frame = data; - priv->is_native = !!is_native; + priv->frame = frame; + priv->needs_swap = !!needs_swap; return self; } @@ -145,7 +145,7 @@ sysprof_document_frame_get_cpu (SysprofDocumentFrame *self) g_return_val_if_fail (SYSPROF_IS_DOCUMENT_FRAME (self), 0); - if G_LIKELY (priv->is_native) + if G_LIKELY (priv->needs_swap) ret = priv->frame->cpu; else ret = GUINT32_SWAP_LE_BE (priv->frame->cpu); @@ -161,7 +161,7 @@ sysprof_document_frame_get_pid (SysprofDocumentFrame *self) g_return_val_if_fail (SYSPROF_IS_DOCUMENT_FRAME (self), 0); - if G_LIKELY (priv->is_native) + if G_LIKELY (priv->needs_swap) ret = priv->frame->pid; else ret = GUINT32_SWAP_LE_BE (priv->frame->pid); @@ -177,7 +177,7 @@ sysprof_document_frame_get_time (SysprofDocumentFrame *self) g_return_val_if_fail (SYSPROF_IS_DOCUMENT_FRAME (self), 0); - if G_LIKELY (priv->is_native) + if G_LIKELY (priv->needs_swap) ret = priv->frame->time; else ret = GUINT32_SWAP_LE_BE (priv->frame->time); diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index 588d9b75..43d5d468 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -33,7 +33,7 @@ struct _SysprofDocument GPtrArray *frames; GMappedFile *mapped_file; SysprofCaptureFileHeader header; - guint is_native : 1; + guint needs_swap : 1; }; static GType @@ -57,9 +57,9 @@ sysprof_document_get_item (GListModel *model, if (position >= self->frames->len) return NULL; - return sysprof_document_frame_new (self->mapped_file, - g_ptr_array_index (self->frames, position), - self->is_native); + return _sysprof_document_frame_new (self->mapped_file, + g_ptr_array_index (self->frames, position), + self->needs_swap); } static void @@ -121,12 +121,12 @@ sysprof_document_load (SysprofDocument *self, /* Keep a copy of our header */ memcpy (&self->header, data, sizeof self->header); #if G_BYTE_ORDER == G_LITTLE_ENDIAN - self->is_native = !!self->header.little_endian; + self->needs_swap = !self->header.little_endian; #else - self->is_native = !self->header.little_endian; + self->needs_swap = !!self->header.little_endian; #endif - if (!self->is_native) + if (self->needs_swap) { self->header.time = GUINT64_SWAP_LE_BE (self->header.time); self->header.end_time = GUINT64_SWAP_LE_BE (self->header.end_time); @@ -138,8 +138,8 @@ sysprof_document_load (SysprofDocument *self, guint16 frame_len; memcpy (&frame_len, &data[pos], sizeof frame_len); - if (!self->is_native) - frame_len = GUINT16_SWAP_LE_BE (self->is_native); + if (self->needs_swap) + frame_len = GUINT16_SWAP_LE_BE (frame_len); if (frame_len < sizeof (SysprofCaptureFrame)) break; From e9d5cb733db1993d0afd16ae301c7b3ccba612d4 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 25 Apr 2023 16:19:54 -0700 Subject: [PATCH 0011/1030] libsysprof-analyze: move to internal types for documents We want to have an object hierarchy, but we don't want to expose our class or other internal details to the public API/ABI. This uses the old form of class definition so we can maintain that. An alternative would be to do what GDK does for internal types, should we find that easier to maintain going forward. --- .../sysprof-document-frame-private.h | 8 ++++ .../sysprof-document-frame.c | 45 +++++++------------ .../sysprof-document-frame.h | 11 +++-- 3 files changed, 32 insertions(+), 32 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-document-frame-private.h b/src/libsysprof-analyze/sysprof-document-frame-private.h index 9e0ea4c5..f2786c1d 100644 --- a/src/libsysprof-analyze/sysprof-document-frame-private.h +++ b/src/libsysprof-analyze/sysprof-document-frame-private.h @@ -26,6 +26,14 @@ G_BEGIN_DECLS +struct _SysprofDocumentFrame +{ + GObject parent_instance; + GMappedFile *mapped_file; + const SysprofCaptureFrame *frame; + guint needs_swap : 1; +}; + struct _SysprofDocumentFrameClass { GObjectClass parent_class; diff --git a/src/libsysprof-analyze/sysprof-document-frame.c b/src/libsysprof-analyze/sysprof-document-frame.c index e3e8bad5..fdd773ee 100644 --- a/src/libsysprof-analyze/sysprof-document-frame.c +++ b/src/libsysprof-analyze/sysprof-document-frame.c @@ -22,14 +22,7 @@ #include "sysprof-document-frame-private.h" -typedef struct _SysprofDocumentFramePrivate -{ - GMappedFile *mapped_file; - const SysprofCaptureFrame *frame; - guint needs_swap : 1; -} SysprofDocumentFramePrivate; - -G_DEFINE_TYPE_WITH_PRIVATE (SysprofDocumentFrame, sysprof_document_frame, G_TYPE_OBJECT) +G_DEFINE_TYPE (SysprofDocumentFrame, sysprof_document_frame, G_TYPE_OBJECT) enum { PROP_0, @@ -45,12 +38,11 @@ static void sysprof_document_frame_finalize (GObject *object) { SysprofDocumentFrame *self = (SysprofDocumentFrame *)object; - SysprofDocumentFramePrivate *priv = sysprof_document_frame_get_instance_private (self); - g_clear_pointer (&priv->mapped_file, g_mapped_file_unref); + g_clear_pointer (&self->mapped_file, g_mapped_file_unref); - priv->frame = NULL; - priv->needs_swap = 0; + self->frame = NULL; + self->needs_swap = 0; G_OBJECT_CLASS (sysprof_document_frame_parent_class)->finalize (object); } @@ -125,14 +117,12 @@ _sysprof_document_frame_new (GMappedFile *mapped_file, gboolean needs_swap) { SysprofDocumentFrame *self; - SysprofDocumentFramePrivate *priv; self = g_object_new (SYSPROF_TYPE_DOCUMENT_FRAME, NULL); - priv = sysprof_document_frame_get_instance_private (self); - priv->mapped_file = g_mapped_file_ref (mapped_file); - priv->frame = frame; - priv->needs_swap = !!needs_swap; + self->mapped_file = g_mapped_file_ref (mapped_file); + self->frame = frame; + self->needs_swap = !!needs_swap; return self; } @@ -140,15 +130,14 @@ _sysprof_document_frame_new (GMappedFile *mapped_file, int sysprof_document_frame_get_cpu (SysprofDocumentFrame *self) { - SysprofDocumentFramePrivate *priv = sysprof_document_frame_get_instance_private (self); int ret; g_return_val_if_fail (SYSPROF_IS_DOCUMENT_FRAME (self), 0); - if G_LIKELY (priv->needs_swap) - ret = priv->frame->cpu; + if G_LIKELY (self->needs_swap) + ret = self->frame->cpu; else - ret = GUINT32_SWAP_LE_BE (priv->frame->cpu); + ret = GUINT32_SWAP_LE_BE (self->frame->cpu); return ret; } @@ -156,15 +145,14 @@ sysprof_document_frame_get_cpu (SysprofDocumentFrame *self) int sysprof_document_frame_get_pid (SysprofDocumentFrame *self) { - SysprofDocumentFramePrivate *priv = sysprof_document_frame_get_instance_private (self); int ret; g_return_val_if_fail (SYSPROF_IS_DOCUMENT_FRAME (self), 0); - if G_LIKELY (priv->needs_swap) - ret = priv->frame->pid; + if G_LIKELY (self->needs_swap) + ret = self->frame->pid; else - ret = GUINT32_SWAP_LE_BE (priv->frame->pid); + ret = GUINT32_SWAP_LE_BE (self->frame->pid); return ret; } @@ -172,15 +160,14 @@ sysprof_document_frame_get_pid (SysprofDocumentFrame *self) gint64 sysprof_document_frame_get_time (SysprofDocumentFrame *self) { - SysprofDocumentFramePrivate *priv = sysprof_document_frame_get_instance_private (self); gint64 ret; g_return_val_if_fail (SYSPROF_IS_DOCUMENT_FRAME (self), 0); - if G_LIKELY (priv->needs_swap) - ret = priv->frame->time; + if G_LIKELY (self->needs_swap) + ret = self->frame->time; else - ret = GUINT32_SWAP_LE_BE (priv->frame->time); + ret = GUINT32_SWAP_LE_BE (self->frame->time); return ret; } diff --git a/src/libsysprof-analyze/sysprof-document-frame.h b/src/libsysprof-analyze/sysprof-document-frame.h index c8e81b6e..ffc31120 100644 --- a/src/libsysprof-analyze/sysprof-document-frame.h +++ b/src/libsysprof-analyze/sysprof-document-frame.h @@ -24,11 +24,16 @@ G_BEGIN_DECLS -#define SYSPROF_TYPE_DOCUMENT_FRAME (sysprof_document_frame_get_type()) +#define SYSPROF_TYPE_DOCUMENT_FRAME (sysprof_document_frame_get_type()) +#define SYSPROF_IS_DOCUMENT_FRAME(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_DOCUMENT_FRAME) +#define SYSPROF_DOCUMENT_FRAME(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_DOCUMENT_FRAME, SysprofDocumentFrame) +#define SYSPROF_DOCUMENT_FRAME_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_DOCUMENT_FRAME, SysprofDocumentFrameClass) + +typedef struct _SysprofDocumentFrame SysprofDocumentFrame; +typedef struct _SysprofDocumentFrameClass SysprofDocumentFrameClass; SYSPROF_AVAILABLE_IN_ALL -G_DECLARE_DERIVABLE_TYPE (SysprofDocumentFrame, sysprof_document_frame, SYSPROF, DOCUMENT_FRAME, GObject) - +GType sysprof_document_frame_get_type (void) G_GNUC_CONST; SYSPROF_AVAILABLE_IN_ALL int sysprof_document_frame_get_cpu (SysprofDocumentFrame *self); SYSPROF_AVAILABLE_IN_ALL From 62863f08c39f15930b7a444a9ce9c6cefe7c805e Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 25 Apr 2023 17:08:13 -0700 Subject: [PATCH 0012/1030] libsysprof-analyze: add various bswap and cast helpers --- .../sysprof-document-frame-private.h | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-document-frame-private.h b/src/libsysprof-analyze/sysprof-document-frame-private.h index f2786c1d..aadd6319 100644 --- a/src/libsysprof-analyze/sysprof-document-frame-private.h +++ b/src/libsysprof-analyze/sysprof-document-frame-private.h @@ -43,4 +43,38 @@ SysprofDocumentFrame *_sysprof_document_frame_new (GMappedFile *ma const SysprofCaptureFrame *frame, gboolean needs_swap); +#define SYSPROF_DOCUMENT_FRAME_GET(obj, type) \ + ((const type *)(SYSPROF_DOCUMENT_FRAME(obj)->frame)) + +#define SYSPROF_DOCUMENT_FRAME_NEEDS_SWAP(obj) \ + (SYSPROF_DOCUMENT_FRAME (obj)->needs_swap) + +#if G_BYTE_ORDER == G_LITTLE_ENDIAN +# define SYSPROF_DOCUMENT_FRAME_UINT16(obj, val) \ + (SYSPROF_DOCUMENT_FRAME_NEEDS_SWAP(obj) ? GUINT16_TO_LE(val) : (val)) +# define SYSPROF_DOCUMENT_FRAME_INT16(obj, val) \ + (SYSPROF_DOCUMENT_FRAME_NEEDS_SWAP(obj) ? GINT16_TO_LE(val) : (val)) +# define SYSPROF_DOCUMENT_FRAME_UINT32(obj, val) \ + (SYSPROF_DOCUMENT_FRAME_NEEDS_SWAP(obj) ? GUINT32_TO_LE(val) : (val)) +# define SYSPROF_DOCUMENT_FRAME_INT32(obj, val) \ + (SYSPROF_DOCUMENT_FRAME_NEEDS_SWAP(obj) ? GINT32_TO_LE(val) : (val)) +# define SYSPROF_DOCUMENT_FRAME_UINT64(obj, val) \ + (SYSPROF_DOCUMENT_FRAME_NEEDS_SWAP(obj) ? GUINT64_TO_LE(val) : (val)) +# define SYSPROF_DOCUMENT_FRAME_INT64(obj, val) \ + (SYSPROF_DOCUMENT_FRAME_NEEDS_SWAP(obj) ? GINT64_TO_LE(val) : (val)) +#else +# define SYSPROF_DOCUMENT_FRAME_UINT16(obj, val) \ + (SYSPROF_DOCUMENT_FRAME_NEEDS_SWAP(obj) ? GUINT16_TO_BE(val) : (val)) +# define SYSPROF_DOCUMENT_FRAME_INT16(obj, val) \ + (SYSPROF_DOCUMENT_FRAME_NEEDS_SWAP(obj) ? GINT16_TO_BE(val) : (val)) +# define SYSPROF_DOCUMENT_FRAME_UINT32(obj, val) \ + (SYSPROF_DOCUMENT_FRAME_NEEDS_SWAP(obj) ? GUINT32_TO_BE(val) : (val)) +# define SYSPROF_DOCUMENT_FRAME_INT32(obj, val) \ + (SYSPROF_DOCUMENT_FRAME_NEEDS_SWAP(obj) ? GINT32_TO_BE(val) : (val)) +# define SYSPROF_DOCUMENT_FRAME_UINT64(obj, val) \ + (SYSPROF_DOCUMENT_FRAME_NEEDS_SWAP(obj) ? GUINT64_TO_BE(val) : (val)) +# define SYSPROF_DOCUMENT_FRAME_INT64(obj, val) \ + (SYSPROF_DOCUMENT_FRAME_NEEDS_SWAP(obj) ? GINT64_TO_BE(val) : (val)) +#endif + G_END_DECLS From 97c93ea9657d7556aecbb53b2352cc7639f5cf5c Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 25 Apr 2023 17:09:04 -0700 Subject: [PATCH 0013/1030] libsysprof-analyze: track frame length That way we don't have to decode this too in the subclasses. --- .../sysprof-document-frame-private.h | 7 ++-- .../sysprof-document-frame.c | 2 ++ src/libsysprof-analyze/sysprof-document.c | 33 +++++++++++++------ 3 files changed, 30 insertions(+), 12 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-document-frame-private.h b/src/libsysprof-analyze/sysprof-document-frame-private.h index aadd6319..918acb9d 100644 --- a/src/libsysprof-analyze/sysprof-document-frame-private.h +++ b/src/libsysprof-analyze/sysprof-document-frame-private.h @@ -28,10 +28,12 @@ G_BEGIN_DECLS struct _SysprofDocumentFrame { - GObject parent_instance; + GObject parent; GMappedFile *mapped_file; const SysprofCaptureFrame *frame; - guint needs_swap : 1; + guint32 frame_len : 16; + guint32 needs_swap : 1; + guint32 padding : 15; }; struct _SysprofDocumentFrameClass @@ -41,6 +43,7 @@ struct _SysprofDocumentFrameClass SysprofDocumentFrame *_sysprof_document_frame_new (GMappedFile *mapped, const SysprofCaptureFrame *frame, + guint16 frame_len, gboolean needs_swap); #define SYSPROF_DOCUMENT_FRAME_GET(obj, type) \ diff --git a/src/libsysprof-analyze/sysprof-document-frame.c b/src/libsysprof-analyze/sysprof-document-frame.c index fdd773ee..3bd4ddcb 100644 --- a/src/libsysprof-analyze/sysprof-document-frame.c +++ b/src/libsysprof-analyze/sysprof-document-frame.c @@ -114,6 +114,7 @@ sysprof_document_frame_init (SysprofDocumentFrame *self) SysprofDocumentFrame * _sysprof_document_frame_new (GMappedFile *mapped_file, const SysprofCaptureFrame *frame, + guint16 frame_len, gboolean needs_swap) { SysprofDocumentFrame *self; @@ -122,6 +123,7 @@ _sysprof_document_frame_new (GMappedFile *mapped_file, self->mapped_file = g_mapped_file_ref (mapped_file); self->frame = frame; + self->frame_len = frame_len; self->needs_swap = !!needs_swap; return self; diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index 43d5d468..9b9ed75b 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -30,12 +30,19 @@ struct _SysprofDocument { GObject parent_instance; - GPtrArray *frames; + GArray *frames; GMappedFile *mapped_file; + const guint8 *base; SysprofCaptureFileHeader header; guint needs_swap : 1; }; +typedef struct _SysprofDocumentFramePointer +{ + guint64 offset : 48; + guint64 length : 16; +} SysprofDocumentFramePointer; + static GType sysprof_document_get_item_type (GListModel *model) { @@ -50,15 +57,19 @@ sysprof_document_get_n_items (GListModel *model) static gpointer sysprof_document_get_item (GListModel *model, - guint position) + guint position) { SysprofDocument *self = SYSPROF_DOCUMENT (model); + SysprofDocumentFramePointer *ptr; if (position >= self->frames->len) return NULL; + ptr = &g_array_index (self->frames, SysprofDocumentFramePointer, position); + return _sysprof_document_frame_new (self->mapped_file, - g_ptr_array_index (self->frames, position), + (gconstpointer)&self->base[ptr->offset], + ptr->length, self->needs_swap); } @@ -79,7 +90,7 @@ sysprof_document_finalize (GObject *object) SysprofDocument *self = (SysprofDocument *)object; g_clear_pointer (&self->mapped_file, g_mapped_file_unref); - g_clear_pointer (&self->frames, g_ptr_array_unref); + g_clear_pointer (&self->frames, g_array_unref); G_OBJECT_CLASS (sysprof_document_parent_class)->finalize (object); } @@ -94,7 +105,7 @@ sysprof_document_class_init (SysprofDocumentClass *klass) static void sysprof_document_init (SysprofDocument *self) { - self->frames = g_ptr_array_new (); + self->frames = g_array_new (FALSE, FALSE, sizeof (SysprofDocumentFramePointer)); } static gboolean @@ -102,7 +113,6 @@ sysprof_document_load (SysprofDocument *self, int capture_fd, GError **error) { - const guint8 *data; goffset pos; gsize len; @@ -112,14 +122,14 @@ sysprof_document_load (SysprofDocument *self, if (!(self->mapped_file = g_mapped_file_new_from_fd (capture_fd, FALSE, error))) return FALSE; - data = (const guint8 *)g_mapped_file_get_contents (self->mapped_file); + self->base = (const guint8 *)g_mapped_file_get_contents (self->mapped_file); len = g_mapped_file_get_length (self->mapped_file); if (len < sizeof self->header) return FALSE; /* Keep a copy of our header */ - memcpy (&self->header, data, sizeof self->header); + memcpy (&self->header, self->base, sizeof self->header); #if G_BYTE_ORDER == G_LITTLE_ENDIAN self->needs_swap = !self->header.little_endian; #else @@ -135,16 +145,19 @@ sysprof_document_load (SysprofDocument *self, pos = sizeof self->header; while (pos < (len - sizeof(guint16))) { + SysprofDocumentFramePointer ptr; guint16 frame_len; - memcpy (&frame_len, &data[pos], sizeof frame_len); + memcpy (&frame_len, &self->base[pos], sizeof frame_len); if (self->needs_swap) frame_len = GUINT16_SWAP_LE_BE (frame_len); if (frame_len < sizeof (SysprofCaptureFrame)) break; - g_ptr_array_add (self->frames, (gpointer)&data[pos]); + ptr.offset = pos; + ptr.length = frame_len; + g_array_append_val (self->frames, ptr); pos += frame_len; } From a47627e5b9adf167284250463f90bee4a6033816 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 25 Apr 2023 17:09:38 -0700 Subject: [PATCH 0014/1030] libsysprof-analyze: use swap helpers to decode --- .../sysprof-document-frame.c | 27 +++---------------- 1 file changed, 3 insertions(+), 24 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-document-frame.c b/src/libsysprof-analyze/sysprof-document-frame.c index 3bd4ddcb..fa7180e4 100644 --- a/src/libsysprof-analyze/sysprof-document-frame.c +++ b/src/libsysprof-analyze/sysprof-document-frame.c @@ -132,44 +132,23 @@ _sysprof_document_frame_new (GMappedFile *mapped_file, int sysprof_document_frame_get_cpu (SysprofDocumentFrame *self) { - int ret; - g_return_val_if_fail (SYSPROF_IS_DOCUMENT_FRAME (self), 0); - if G_LIKELY (self->needs_swap) - ret = self->frame->cpu; - else - ret = GUINT32_SWAP_LE_BE (self->frame->cpu); - - return ret; + return SYSPROF_DOCUMENT_FRAME_INT32 (self, self->frame->cpu); } int sysprof_document_frame_get_pid (SysprofDocumentFrame *self) { - int ret; - g_return_val_if_fail (SYSPROF_IS_DOCUMENT_FRAME (self), 0); - if G_LIKELY (self->needs_swap) - ret = self->frame->pid; - else - ret = GUINT32_SWAP_LE_BE (self->frame->pid); - - return ret; + return SYSPROF_DOCUMENT_FRAME_INT32 (self, self->frame->pid); } gint64 sysprof_document_frame_get_time (SysprofDocumentFrame *self) { - gint64 ret; - g_return_val_if_fail (SYSPROF_IS_DOCUMENT_FRAME (self), 0); - if G_LIKELY (self->needs_swap) - ret = self->frame->time; - else - ret = GUINT32_SWAP_LE_BE (self->frame->time); - - return ret; + return SYSPROF_DOCUMENT_FRAME_INT64 (self, self->frame->time); } From ca83cd6b401356ac9ad617c034eb01f0fb708c8c Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 25 Apr 2023 17:10:12 -0700 Subject: [PATCH 0015/1030] libsysprof-analyze: add document type for samples --- src/libsysprof-analyze/meson.build | 2 + src/libsysprof-analyze/sysprof-analyze.h | 1 + .../sysprof-document-frame.c | 6 +- .../sysprof-document-sample.c | 133 ++++++++++++++++++ .../sysprof-document-sample.h | 42 ++++++ .../tests/test-capture-model.c | 5 +- 6 files changed, 186 insertions(+), 3 deletions(-) create mode 100644 src/libsysprof-analyze/sysprof-document-sample.c create mode 100644 src/libsysprof-analyze/sysprof-document-sample.h diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build index d803f25a..ba127f41 100644 --- a/src/libsysprof-analyze/meson.build +++ b/src/libsysprof-analyze/meson.build @@ -1,12 +1,14 @@ libsysprof_analyze_public_sources = [ 'sysprof-document.c', 'sysprof-document-frame.c', + 'sysprof-document-sample.c', ] libsysprof_analyze_public_headers = [ 'sysprof-analyze.h', 'sysprof-document.h', 'sysprof-document-frame.h', + 'sysprof-document-sample.h', ] libsysprof_analyze_deps = [ diff --git a/src/libsysprof-analyze/sysprof-analyze.h b/src/libsysprof-analyze/sysprof-analyze.h index 9f6a9860..ef81d837 100644 --- a/src/libsysprof-analyze/sysprof-analyze.h +++ b/src/libsysprof-analyze/sysprof-analyze.h @@ -27,6 +27,7 @@ G_BEGIN_DECLS #define SYSPROF_ANALYZE_INSIDE # include "sysprof-document.h" # include "sysprof-document-frame.h" +# include "sysprof-document-sample.h" #undef SYSPROF_ANALYZE_INSIDE G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-document-frame.c b/src/libsysprof-analyze/sysprof-document-frame.c index fa7180e4..cc976e4f 100644 --- a/src/libsysprof-analyze/sysprof-document-frame.c +++ b/src/libsysprof-analyze/sysprof-document-frame.c @@ -21,6 +21,7 @@ #include "config.h" #include "sysprof-document-frame-private.h" +#include "sysprof-document-sample.h" G_DEFINE_TYPE (SysprofDocumentFrame, sysprof_document_frame, G_TYPE_OBJECT) @@ -117,10 +118,13 @@ _sysprof_document_frame_new (GMappedFile *mapped_file, guint16 frame_len, gboolean needs_swap) { + GType gtype = SYSPROF_TYPE_DOCUMENT_FRAME; SysprofDocumentFrame *self; - self = g_object_new (SYSPROF_TYPE_DOCUMENT_FRAME, NULL); + if (frame->type == SYSPROF_CAPTURE_FRAME_SAMPLE) + gtype = SYSPROF_TYPE_DOCUMENT_SAMPLE; + self = g_object_new (gtype, NULL); self->mapped_file = g_mapped_file_ref (mapped_file); self->frame = frame; self->frame_len = frame_len; diff --git a/src/libsysprof-analyze/sysprof-document-sample.c b/src/libsysprof-analyze/sysprof-document-sample.c new file mode 100644 index 00000000..8fbbe2d4 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-document-sample.c @@ -0,0 +1,133 @@ +/* sysprof-document-sample.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-document-frame-private.h" +#include "sysprof-document-sample.h" + +struct _SysprofDocumentSample +{ + SysprofDocumentFrame parent_instance; +}; + +struct _SysprofDocumentSampleClass +{ + SysprofDocumentFrameClass parent_class; +}; + +enum { + PROP_0, + PROP_DEPTH, + PROP_TID, + N_PROPS +}; + +G_DEFINE_FINAL_TYPE (SysprofDocumentSample, sysprof_document_sample, SYSPROF_TYPE_DOCUMENT_FRAME) + +static GParamSpec *properties [N_PROPS]; + +static void +sysprof_document_sample_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofDocumentSample *self = SYSPROF_DOCUMENT_SAMPLE (object); + + switch (prop_id) + { + case PROP_DEPTH: + g_value_set_uint (value, sysprof_document_sample_get_depth (self)); + break; + + case PROP_TID: + g_value_set_int (value, sysprof_document_sample_get_tid (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_document_sample_class_init (SysprofDocumentSampleClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->get_property = sysprof_document_sample_get_property; + + /** + * SysprofDocumentSample:tid: + * + * The task-id or thread-id of the thread which was sampled. + * + * On Linux, this is generally set to the value of the gettid() syscall. + * + * Since: 45 + */ + properties [PROP_TID] = + g_param_spec_int ("tid", NULL, NULL, + G_MININT32, G_MAXINT32, 0, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + /** + * SysprofDocumentSample:depth: + * + * The depth of the stack trace. + * + * Since: 45 + */ + properties [PROP_DEPTH] = + g_param_spec_uint ("depth", NULL, NULL, + 0, G_MAXUINT32, 0, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); +} + +static void +sysprof_document_sample_init (SysprofDocumentSample *self) +{ +} + +guint +sysprof_document_sample_get_depth (SysprofDocumentSample *self) +{ + const SysprofCaptureSample *sample; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_SAMPLE (self), 0); + + sample = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureSample); + + return SYSPROF_DOCUMENT_FRAME_UINT32 (self, sample->n_addrs); +} + +int +sysprof_document_sample_get_tid (SysprofDocumentSample *self) +{ + const SysprofCaptureSample *sample; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_SAMPLE (self), 0); + + sample = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureSample); + + return SYSPROF_DOCUMENT_FRAME_INT32 (self, sample->tid); +} diff --git a/src/libsysprof-analyze/sysprof-document-sample.h b/src/libsysprof-analyze/sysprof-document-sample.h new file mode 100644 index 00000000..1155626e --- /dev/null +++ b/src/libsysprof-analyze/sysprof-document-sample.h @@ -0,0 +1,42 @@ +/* sysprof-document-sample.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-document-sample.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_DOCUMENT_SAMPLE (sysprof_document_sample_get_type()) +#define SYSPROF_IS_DOCUMENT_SAMPLE(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_DOCUMENT_SAMPLE) +#define SYSPROF_DOCUMENT_SAMPLE(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_DOCUMENT_SAMPLE, SysprofDocumentSample) +#define SYSPROF_DOCUMENT_SAMPLE_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_DOCUMENT_SAMPLE, SysprofDocumentSampleClass) + +typedef struct _SysprofDocumentSample SysprofDocumentSample; +typedef struct _SysprofDocumentSampleClass SysprofDocumentSampleClass; + +SYSPROF_AVAILABLE_IN_ALL +GType sysprof_document_sample_get_type (void) G_GNUC_CONST; +SYSPROF_AVAILABLE_IN_ALL +guint sysprof_document_sample_get_depth (SysprofDocumentSample *self); +SYSPROF_AVAILABLE_IN_ALL +int sysprof_document_sample_get_tid (SysprofDocumentSample *self); + +G_END_DECLS diff --git a/src/libsysprof-analyze/tests/test-capture-model.c b/src/libsysprof-analyze/tests/test-capture-model.c index dba4918b..8fb3b1ad 100644 --- a/src/libsysprof-analyze/tests/test-capture-model.c +++ b/src/libsysprof-analyze/tests/test-capture-model.c @@ -36,10 +36,11 @@ main (int argc, { SysprofDocumentFrame *frame = g_list_model_get_item (G_LIST_MODEL (document), i); - g_print ("%"G_GINT64_FORMAT" [pid %d] [cpu %d]\n", + g_print ("%"G_GINT64_FORMAT" [pid %d] [cpu %d] (type %s)\n", sysprof_document_frame_get_time (frame), sysprof_document_frame_get_pid (frame), - sysprof_document_frame_get_cpu (frame)); + sysprof_document_frame_get_cpu (frame), + G_OBJECT_TYPE_NAME (frame)); g_clear_object (&frame); } From 1182b65c078091c5766cda93e5ec4307dc5e78d6 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 27 Apr 2023 16:59:41 -0700 Subject: [PATCH 0016/1030] libsysprof-analyze: add CString helper This checks, up to the end of the frame, that we have a valid \0 for the CString before passing it back to the caller. Otherwise, NULL is returned for a corrupt/invalid CString within the frame. --- .../sysprof-document-frame-private.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-document-frame-private.h b/src/libsysprof-analyze/sysprof-document-frame-private.h index 918acb9d..96949ac4 100644 --- a/src/libsysprof-analyze/sysprof-document-frame-private.h +++ b/src/libsysprof-analyze/sysprof-document-frame-private.h @@ -80,4 +80,19 @@ SysprofDocumentFrame *_sysprof_document_frame_new (GMappedFile *ma (SYSPROF_DOCUMENT_FRAME_NEEDS_SWAP(obj) ? GINT64_TO_BE(val) : (val)) #endif +static inline const char * +SYSPROF_DOCUMENT_FRAME_CSTRING (SysprofDocumentFrame *self, + const char *str) +{ + const char *endptr = (const char *)self->frame + self->frame_len; + + for (const char *c = str; c < endptr; c++) + { + if (*c == 0) + return str; + } + + return NULL; +} + G_END_DECLS From 4f74e92523add065e7755da60278fa867be31ee6 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 27 Apr 2023 16:59:57 -0700 Subject: [PATCH 0017/1030] libsysprof-analyze: fix recursive include --- src/libsysprof-analyze/sysprof-document-sample.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libsysprof-analyze/sysprof-document-sample.h b/src/libsysprof-analyze/sysprof-document-sample.h index 1155626e..9a8906da 100644 --- a/src/libsysprof-analyze/sysprof-document-sample.h +++ b/src/libsysprof-analyze/sysprof-document-sample.h @@ -20,7 +20,7 @@ #pragma once -#include "sysprof-document-sample.h" +#include "sysprof-document-frame.h" G_BEGIN_DECLS From c1d00db0fb135a136da3aa1e854ec298b0adab30 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 27 Apr 2023 17:00:18 -0700 Subject: [PATCH 0018/1030] libsysprof-analyze: add document type for memory maps --- src/libsysprof-analyze/meson.build | 2 + src/libsysprof-analyze/sysprof-analyze.h | 1 + .../sysprof-document-frame.c | 6 +- .../sysprof-document-mmap.c | 184 ++++++++++++++++++ .../sysprof-document-mmap.h | 49 +++++ 5 files changed, 241 insertions(+), 1 deletion(-) create mode 100644 src/libsysprof-analyze/sysprof-document-mmap.c create mode 100644 src/libsysprof-analyze/sysprof-document-mmap.h diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build index ba127f41..1007ab44 100644 --- a/src/libsysprof-analyze/meson.build +++ b/src/libsysprof-analyze/meson.build @@ -1,6 +1,7 @@ libsysprof_analyze_public_sources = [ 'sysprof-document.c', 'sysprof-document-frame.c', + 'sysprof-document-mmap.c', 'sysprof-document-sample.c', ] @@ -8,6 +9,7 @@ libsysprof_analyze_public_headers = [ 'sysprof-analyze.h', 'sysprof-document.h', 'sysprof-document-frame.h', + 'sysprof-document-mmap.h', 'sysprof-document-sample.h', ] diff --git a/src/libsysprof-analyze/sysprof-analyze.h b/src/libsysprof-analyze/sysprof-analyze.h index ef81d837..0ecdd0f4 100644 --- a/src/libsysprof-analyze/sysprof-analyze.h +++ b/src/libsysprof-analyze/sysprof-analyze.h @@ -27,6 +27,7 @@ G_BEGIN_DECLS #define SYSPROF_ANALYZE_INSIDE # include "sysprof-document.h" # include "sysprof-document-frame.h" +# include "sysprof-document-mmap.h" # include "sysprof-document-sample.h" #undef SYSPROF_ANALYZE_INSIDE diff --git a/src/libsysprof-analyze/sysprof-document-frame.c b/src/libsysprof-analyze/sysprof-document-frame.c index cc976e4f..cd1b86f3 100644 --- a/src/libsysprof-analyze/sysprof-document-frame.c +++ b/src/libsysprof-analyze/sysprof-document-frame.c @@ -21,6 +21,7 @@ #include "config.h" #include "sysprof-document-frame-private.h" +#include "sysprof-document-mmap.h" #include "sysprof-document-sample.h" G_DEFINE_TYPE (SysprofDocumentFrame, sysprof_document_frame, G_TYPE_OBJECT) @@ -121,7 +122,10 @@ _sysprof_document_frame_new (GMappedFile *mapped_file, GType gtype = SYSPROF_TYPE_DOCUMENT_FRAME; SysprofDocumentFrame *self; - if (frame->type == SYSPROF_CAPTURE_FRAME_SAMPLE) + if (0) {} + else if (frame->type == SYSPROF_CAPTURE_FRAME_MAP) + gtype = SYSPROF_TYPE_DOCUMENT_MMAP; + else if (frame->type == SYSPROF_CAPTURE_FRAME_SAMPLE) gtype = SYSPROF_TYPE_DOCUMENT_SAMPLE; self = g_object_new (gtype, NULL); diff --git a/src/libsysprof-analyze/sysprof-document-mmap.c b/src/libsysprof-analyze/sysprof-document-mmap.c new file mode 100644 index 00000000..8a5c99e3 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-document-mmap.c @@ -0,0 +1,184 @@ +/* + * sysprof-document-mmap.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-document-frame-private.h" +#include "sysprof-document-mmap.h" + +struct _SysprofDocumentMmap +{ + SysprofDocumentFrame parent_instance; +}; + +struct _SysprofDocumentMmapClass +{ + SysprofDocumentFrameClass parent_class; +}; + +enum { + PROP_0, + PROP_END_ADDRESS, + PROP_FILE, + PROP_FILE_INODE, + PROP_FILE_OFFSET, + PROP_START_ADDRESS, + N_PROPS +}; + +G_DEFINE_FINAL_TYPE (SysprofDocumentMmap, sysprof_document_mmap, SYSPROF_TYPE_DOCUMENT_FRAME) + +static GParamSpec *properties [N_PROPS]; + +static void +sysprof_document_mmap_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofDocumentMmap *self = SYSPROF_DOCUMENT_MMAP (object); + + switch (prop_id) + { + case PROP_START_ADDRESS: + g_value_set_uint64 (value, sysprof_document_mmap_get_start_address (self)); + break; + + case PROP_END_ADDRESS: + g_value_set_uint64 (value, sysprof_document_mmap_get_end_address (self)); + break; + + case PROP_FILE: + g_value_set_string (value, sysprof_document_mmap_get_file (self)); + break; + + case PROP_FILE_OFFSET: + g_value_set_uint64 (value, sysprof_document_mmap_get_file_offset (self)); + break; + + case PROP_FILE_INODE: + g_value_set_uint64 (value, sysprof_document_mmap_get_file_inode (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_document_mmap_class_init (SysprofDocumentMmapClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->get_property = sysprof_document_mmap_get_property; + + properties [PROP_START_ADDRESS] = + g_param_spec_uint64 ("start-address", NULL, NULL, + 0, G_MAXUINT64, 0, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + properties [PROP_END_ADDRESS] = + g_param_spec_uint64 ("end-address", NULL, NULL, + 0, G_MAXUINT64, 0, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + properties [PROP_FILE] = + g_param_spec_string ("file", NULL, NULL, + NULL, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + properties [PROP_FILE_INODE] = + g_param_spec_uint64 ("file-inode", NULL, NULL, + 0, G_MAXUINT64, 0, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + properties [PROP_FILE_OFFSET] = + g_param_spec_uint64 ("file-offset", NULL, NULL, + 0, G_MAXUINT64, 0, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); +} + +static void +sysprof_document_mmap_init (SysprofDocumentMmap *self) +{ +} + +guint64 +sysprof_document_mmap_get_start_address (SysprofDocumentMmap *self) +{ + const SysprofCaptureMap *mmap; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_MMAP (self), 0); + + mmap = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureMap); + + return SYSPROF_DOCUMENT_FRAME_UINT64 (self, mmap->start); +} + +guint64 +sysprof_document_mmap_get_end_address (SysprofDocumentMmap *self) +{ + const SysprofCaptureMap *mmap; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_MMAP (self), 0); + + mmap = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureMap); + + return SYSPROF_DOCUMENT_FRAME_UINT64 (self, mmap->end); +} + +guint64 +sysprof_document_mmap_get_file_inode (SysprofDocumentMmap *self) +{ + const SysprofCaptureMap *mmap; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_MMAP (self), 0); + + mmap = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureMap); + + return SYSPROF_DOCUMENT_FRAME_UINT64 (self, mmap->inode); +} + +guint64 +sysprof_document_mmap_get_file_offset (SysprofDocumentMmap *self) +{ + const SysprofCaptureMap *mmap; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_MMAP (self), 0); + + mmap = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureMap); + + return SYSPROF_DOCUMENT_FRAME_UINT64 (self, mmap->offset); +} + +const char * +sysprof_document_mmap_get_file (SysprofDocumentMmap *self) +{ + const SysprofCaptureMap *mmap; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_MMAP (self), 0); + + mmap = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureMap); + + return SYSPROF_DOCUMENT_FRAME_CSTRING (SYSPROF_DOCUMENT_FRAME (self), mmap->filename); +} diff --git a/src/libsysprof-analyze/sysprof-document-mmap.h b/src/libsysprof-analyze/sysprof-document-mmap.h new file mode 100644 index 00000000..bdcbf870 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-document-mmap.h @@ -0,0 +1,49 @@ +/* + * sysprof-document-mmap.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-document-frame.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_DOCUMENT_MMAP (sysprof_document_mmap_get_type()) +#define SYSPROF_IS_DOCUMENT_MMAP(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_DOCUMENT_MMAP) +#define SYSPROF_DOCUMENT_MMAP(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_DOCUMENT_MMAP, SysprofDocumentMmap) +#define SYSPROF_DOCUMENT_MMAP_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_DOCUMENT_MMAP, SysprofDocumentMmapClass) + +typedef struct _SysprofDocumentMmap SysprofDocumentMmap; +typedef struct _SysprofDocumentMmapClass SysprofDocumentMmapClass; + +SYSPROF_AVAILABLE_IN_ALL +GType sysprof_document_mmap_get_type (void) G_GNUC_CONST; +SYSPROF_AVAILABLE_IN_ALL +guint64 sysprof_document_mmap_get_start_address (SysprofDocumentMmap *self); +SYSPROF_AVAILABLE_IN_ALL +guint64 sysprof_document_mmap_get_end_address (SysprofDocumentMmap *self); +SYSPROF_AVAILABLE_IN_ALL +guint64 sysprof_document_mmap_get_file_inode (SysprofDocumentMmap *self); +SYSPROF_AVAILABLE_IN_ALL +guint64 sysprof_document_mmap_get_file_offset (SysprofDocumentMmap *self); +SYSPROF_AVAILABLE_IN_ALL +const char *sysprof_document_mmap_get_file (SysprofDocumentMmap *self); + +G_END_DECLS From 9b05f5c0a5011c3fcd9b439bd668c9e4fb4cbd2e Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 27 Apr 2023 17:14:19 -0700 Subject: [PATCH 0019/1030] libsysprof-analyze: make CSTRING helper more succinct to use --- src/libsysprof-analyze/sysprof-document-frame-private.h | 7 +++++-- src/libsysprof-analyze/sysprof-document-mmap.c | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-document-frame-private.h b/src/libsysprof-analyze/sysprof-document-frame-private.h index 96949ac4..91a74a8a 100644 --- a/src/libsysprof-analyze/sysprof-document-frame-private.h +++ b/src/libsysprof-analyze/sysprof-document-frame-private.h @@ -81,8 +81,8 @@ SysprofDocumentFrame *_sysprof_document_frame_new (GMappedFile *ma #endif static inline const char * -SYSPROF_DOCUMENT_FRAME_CSTRING (SysprofDocumentFrame *self, - const char *str) +_SYSPROF_DOCUMENT_FRAME_CSTRING (SysprofDocumentFrame *self, + const char *str) { const char *endptr = (const char *)self->frame + self->frame_len; @@ -95,4 +95,7 @@ SYSPROF_DOCUMENT_FRAME_CSTRING (SysprofDocumentFrame *self, return NULL; } +#define SYSPROF_DOCUMENT_FRAME_CSTRING(obj,str) \ + _SYSPROF_DOCUMENT_FRAME_CSTRING(SYSPROF_DOCUMENT_FRAME(obj),str) + G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-document-mmap.c b/src/libsysprof-analyze/sysprof-document-mmap.c index 8a5c99e3..c5a2f85c 100644 --- a/src/libsysprof-analyze/sysprof-document-mmap.c +++ b/src/libsysprof-analyze/sysprof-document-mmap.c @@ -180,5 +180,5 @@ sysprof_document_mmap_get_file (SysprofDocumentMmap *self) mmap = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureMap); - return SYSPROF_DOCUMENT_FRAME_CSTRING (SYSPROF_DOCUMENT_FRAME (self), mmap->filename); + return SYSPROF_DOCUMENT_FRAME_CSTRING (self, mmap->filename); } From 92950f4b6ba75b004087cec89862fa7bd9f0e4cf Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 27 Apr 2023 17:15:35 -0700 Subject: [PATCH 0020/1030] libsysprof-analyze: add document type for log messages --- src/libsysprof-analyze/meson.build | 2 + src/libsysprof-analyze/sysprof-analyze.h | 1 + .../sysprof-document-frame.c | 7 +- src/libsysprof-analyze/sysprof-document-log.c | 139 ++++++++++++++++++ src/libsysprof-analyze/sysprof-document-log.h | 44 ++++++ 5 files changed, 191 insertions(+), 2 deletions(-) create mode 100644 src/libsysprof-analyze/sysprof-document-log.c create mode 100644 src/libsysprof-analyze/sysprof-document-log.h diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build index 1007ab44..eb5fb0cd 100644 --- a/src/libsysprof-analyze/meson.build +++ b/src/libsysprof-analyze/meson.build @@ -1,6 +1,7 @@ libsysprof_analyze_public_sources = [ 'sysprof-document.c', 'sysprof-document-frame.c', + 'sysprof-document-log.c', 'sysprof-document-mmap.c', 'sysprof-document-sample.c', ] @@ -9,6 +10,7 @@ libsysprof_analyze_public_headers = [ 'sysprof-analyze.h', 'sysprof-document.h', 'sysprof-document-frame.h', + 'sysprof-document-log.h', 'sysprof-document-mmap.h', 'sysprof-document-sample.h', ] diff --git a/src/libsysprof-analyze/sysprof-analyze.h b/src/libsysprof-analyze/sysprof-analyze.h index 0ecdd0f4..1ed5e941 100644 --- a/src/libsysprof-analyze/sysprof-analyze.h +++ b/src/libsysprof-analyze/sysprof-analyze.h @@ -27,6 +27,7 @@ G_BEGIN_DECLS #define SYSPROF_ANALYZE_INSIDE # include "sysprof-document.h" # include "sysprof-document-frame.h" +# include "sysprof-document-log.h" # include "sysprof-document-mmap.h" # include "sysprof-document-sample.h" #undef SYSPROF_ANALYZE_INSIDE diff --git a/src/libsysprof-analyze/sysprof-document-frame.c b/src/libsysprof-analyze/sysprof-document-frame.c index cd1b86f3..fb574c3e 100644 --- a/src/libsysprof-analyze/sysprof-document-frame.c +++ b/src/libsysprof-analyze/sysprof-document-frame.c @@ -21,6 +21,7 @@ #include "config.h" #include "sysprof-document-frame-private.h" +#include "sysprof-document-log.h" #include "sysprof-document-mmap.h" #include "sysprof-document-sample.h" @@ -123,10 +124,12 @@ _sysprof_document_frame_new (GMappedFile *mapped_file, SysprofDocumentFrame *self; if (0) {} - else if (frame->type == SYSPROF_CAPTURE_FRAME_MAP) - gtype = SYSPROF_TYPE_DOCUMENT_MMAP; else if (frame->type == SYSPROF_CAPTURE_FRAME_SAMPLE) gtype = SYSPROF_TYPE_DOCUMENT_SAMPLE; + else if (frame->type == SYSPROF_CAPTURE_FRAME_MAP) + gtype = SYSPROF_TYPE_DOCUMENT_MMAP; + else if (frame->type == SYSPROF_CAPTURE_FRAME_LOG) + gtype = SYSPROF_TYPE_DOCUMENT_LOG; self = g_object_new (gtype, NULL); self->mapped_file = g_mapped_file_ref (mapped_file); diff --git a/src/libsysprof-analyze/sysprof-document-log.c b/src/libsysprof-analyze/sysprof-document-log.c new file mode 100644 index 00000000..ce03db0a --- /dev/null +++ b/src/libsysprof-analyze/sysprof-document-log.c @@ -0,0 +1,139 @@ +/* sysprof-document-log.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-document-frame-private.h" +#include "sysprof-document-log.h" + +struct _SysprofDocumentLog +{ + SysprofDocumentFrame parent_instance; +}; + +struct _SysprofDocumentLogClass +{ + SysprofDocumentFrameClass parent_class; +}; + +enum { + PROP_0, + PROP_DOMAIN, + PROP_MESSAGE, + PROP_SEVERITY, + N_PROPS +}; + +G_DEFINE_FINAL_TYPE (SysprofDocumentLog, sysprof_document_log, SYSPROF_TYPE_DOCUMENT_FRAME) + +static GParamSpec *properties [N_PROPS]; + +static void +sysprof_document_log_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofDocumentLog *self = SYSPROF_DOCUMENT_LOG (object); + + switch (prop_id) + { + case PROP_SEVERITY: + g_value_set_uint (value, sysprof_document_log_get_severity (self)); + break; + + case PROP_MESSAGE: + g_value_set_string (value, sysprof_document_log_get_message (self)); + break; + + case PROP_DOMAIN: + g_value_set_string (value, sysprof_document_log_get_domain (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_document_log_class_init (SysprofDocumentLogClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->get_property = sysprof_document_log_get_property; + + properties [PROP_SEVERITY] = + g_param_spec_uint ("severity", NULL, NULL, + 0, G_MAXUINT16, 0, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + properties [PROP_DOMAIN] = + g_param_spec_string ("domain", NULL, NULL, + NULL, + (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)); + + g_object_class_install_properties (object_class, N_PROPS, properties); +} + +static void +sysprof_document_log_init (SysprofDocumentLog *self) +{ +} + +GLogLevelFlags +sysprof_document_log_get_severity (SysprofDocumentLog *self) +{ + const SysprofCaptureLog *log; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_LOG (self), 0); + + log = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureLog); + + return SYSPROF_DOCUMENT_FRAME_UINT16 (self, log->severity); +} + +const char * +sysprof_document_log_get_message (SysprofDocumentLog *self) +{ + const SysprofCaptureLog *log; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_LOG (self), 0); + + log = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureLog); + + return SYSPROF_DOCUMENT_FRAME_CSTRING (self, log->message); +} + +const char * +sysprof_document_log_get_domain (SysprofDocumentLog *self) +{ + const SysprofCaptureLog *log; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_LOG (self), 0); + + log = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureLog); + + return SYSPROF_DOCUMENT_FRAME_CSTRING (self, log->domain); +} diff --git a/src/libsysprof-analyze/sysprof-document-log.h b/src/libsysprof-analyze/sysprof-document-log.h new file mode 100644 index 00000000..0cbf436d --- /dev/null +++ b/src/libsysprof-analyze/sysprof-document-log.h @@ -0,0 +1,44 @@ +/* sysprof-document-log.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-document-frame.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_DOCUMENT_LOG (sysprof_document_log_get_type()) +#define SYSPROF_IS_DOCUMENT_LOG(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_DOCUMENT_LOG) +#define SYSPROF_DOCUMENT_LOG(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_DOCUMENT_LOG, SysprofDocumentLog) +#define SYSPROF_DOCUMENT_LOG_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_DOCUMENT_LOG, SysprofDocumentLogClass) + +typedef struct _SysprofDocumentLog SysprofDocumentLog; +typedef struct _SysprofDocumentLogClass SysprofDocumentLogClass; + +SYSPROF_AVAILABLE_IN_ALL +GType sysprof_document_log_get_type (void) G_GNUC_CONST; +SYSPROF_AVAILABLE_IN_ALL +const char *sysprof_document_log_get_message (SysprofDocumentLog *self); +SYSPROF_AVAILABLE_IN_ALL +GLogLevelFlags sysprof_document_log_get_severity (SysprofDocumentLog *self); +SYSPROF_AVAILABLE_IN_ALL +const char *sysprof_document_log_get_domain (SysprofDocumentLog *self); + +G_END_DECLS From 6031233cc39bd6c07969bc7b2ef09f1962a0e87a Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 27 Apr 2023 17:22:44 -0700 Subject: [PATCH 0021/1030] libsysprof-analyze: add document type for marks --- src/libsysprof-analyze/meson.build | 2 + src/libsysprof-analyze/sysprof-analyze.h | 1 + .../sysprof-document-frame.c | 3 + .../sysprof-document-mark.c | 161 ++++++++++++++++++ .../sysprof-document-mark.h | 47 +++++ 5 files changed, 214 insertions(+) create mode 100644 src/libsysprof-analyze/sysprof-document-mark.c create mode 100644 src/libsysprof-analyze/sysprof-document-mark.h diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build index eb5fb0cd..471a9ef3 100644 --- a/src/libsysprof-analyze/meson.build +++ b/src/libsysprof-analyze/meson.build @@ -2,6 +2,7 @@ libsysprof_analyze_public_sources = [ 'sysprof-document.c', 'sysprof-document-frame.c', 'sysprof-document-log.c', + 'sysprof-document-mark.c', 'sysprof-document-mmap.c', 'sysprof-document-sample.c', ] @@ -11,6 +12,7 @@ libsysprof_analyze_public_headers = [ 'sysprof-document.h', 'sysprof-document-frame.h', 'sysprof-document-log.h', + 'sysprof-document-mark.h', 'sysprof-document-mmap.h', 'sysprof-document-sample.h', ] diff --git a/src/libsysprof-analyze/sysprof-analyze.h b/src/libsysprof-analyze/sysprof-analyze.h index 1ed5e941..01814bd4 100644 --- a/src/libsysprof-analyze/sysprof-analyze.h +++ b/src/libsysprof-analyze/sysprof-analyze.h @@ -28,6 +28,7 @@ G_BEGIN_DECLS # include "sysprof-document.h" # include "sysprof-document-frame.h" # include "sysprof-document-log.h" +# include "sysprof-document-mark.h" # include "sysprof-document-mmap.h" # include "sysprof-document-sample.h" #undef SYSPROF_ANALYZE_INSIDE diff --git a/src/libsysprof-analyze/sysprof-document-frame.c b/src/libsysprof-analyze/sysprof-document-frame.c index fb574c3e..0f3fd8e6 100644 --- a/src/libsysprof-analyze/sysprof-document-frame.c +++ b/src/libsysprof-analyze/sysprof-document-frame.c @@ -22,6 +22,7 @@ #include "sysprof-document-frame-private.h" #include "sysprof-document-log.h" +#include "sysprof-document-mark.h" #include "sysprof-document-mmap.h" #include "sysprof-document-sample.h" @@ -130,6 +131,8 @@ _sysprof_document_frame_new (GMappedFile *mapped_file, gtype = SYSPROF_TYPE_DOCUMENT_MMAP; else if (frame->type == SYSPROF_CAPTURE_FRAME_LOG) gtype = SYSPROF_TYPE_DOCUMENT_LOG; + else if (frame->type == SYSPROF_CAPTURE_FRAME_MARK) + gtype = SYSPROF_TYPE_DOCUMENT_MARK; self = g_object_new (gtype, NULL); self->mapped_file = g_mapped_file_ref (mapped_file); diff --git a/src/libsysprof-analyze/sysprof-document-mark.c b/src/libsysprof-analyze/sysprof-document-mark.c new file mode 100644 index 00000000..aa91904c --- /dev/null +++ b/src/libsysprof-analyze/sysprof-document-mark.c @@ -0,0 +1,161 @@ +/* sysprof-document-mark.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-document-frame-private.h" +#include "sysprof-document-mark.h" + +struct _SysprofDocumentMark +{ + SysprofDocumentFrame parent_instance; +}; + +struct _SysprofDocumentMarkClass +{ + SysprofDocumentFrameClass parent_class; +}; + +enum { + PROP_0, + PROP_DURATION, + PROP_GROUP, + PROP_MESSAGE, + PROP_NAME, + N_PROPS +}; + +G_DEFINE_FINAL_TYPE (SysprofDocumentMark, sysprof_document_mark, SYSPROF_TYPE_DOCUMENT_FRAME) + +static GParamSpec *properties [N_PROPS]; + +static void +sysprof_document_mark_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofDocumentMark *self = SYSPROF_DOCUMENT_MARK (object); + + switch (prop_id) + { + case PROP_DURATION: + g_value_set_int64 (value, sysprof_document_mark_get_duration (self)); + break; + + case PROP_NAME: + g_value_set_string (value, sysprof_document_mark_get_name (self)); + break; + + case PROP_GROUP: + g_value_set_string (value, sysprof_document_mark_get_group (self)); + break; + + case PROP_MESSAGE: + g_value_set_string (value, sysprof_document_mark_get_message (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_document_mark_class_init (SysprofDocumentMarkClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->get_property = sysprof_document_mark_get_property; + + properties [PROP_DURATION] = + g_param_spec_int64 ("duration", NULL, NULL, + G_MININT64, G_MAXINT64, 0, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + properties [PROP_GROUP] = + g_param_spec_string ("group", NULL, NULL, + NULL, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + properties [PROP_NAME] = + g_param_spec_string ("name", NULL, NULL, + NULL, + (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)); + + g_object_class_install_properties (object_class, N_PROPS, properties); +} + +static void +sysprof_document_mark_init (SysprofDocumentMark *self) +{ +} + +gint64 +sysprof_document_mark_get_duration (SysprofDocumentMark *self) +{ + const SysprofCaptureMark *mark; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_MARK (self), 0); + + mark = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureMark); + + return SYSPROF_DOCUMENT_FRAME_INT64 (self, mark->duration); +} + +const char * +sysprof_document_mark_get_group (SysprofDocumentMark *self) +{ + const SysprofCaptureMark *mark; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_MARK (self), 0); + + mark = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureMark); + + return SYSPROF_DOCUMENT_FRAME_CSTRING (self, mark->group); +} + +const char * +sysprof_document_mark_get_name (SysprofDocumentMark *self) +{ + const SysprofCaptureMark *mark; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_MARK (self), 0); + + mark = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureMark); + + return SYSPROF_DOCUMENT_FRAME_CSTRING (self, mark->name); +} + +const char * +sysprof_document_mark_get_message (SysprofDocumentMark *self) +{ + const SysprofCaptureMark *mark; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_MARK (self), 0); + + mark = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureMark); + + return SYSPROF_DOCUMENT_FRAME_CSTRING (self, mark->message); +} diff --git a/src/libsysprof-analyze/sysprof-document-mark.h b/src/libsysprof-analyze/sysprof-document-mark.h new file mode 100644 index 00000000..a875af30 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-document-mark.h @@ -0,0 +1,47 @@ +/* sysprof-document-mark.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-document-frame.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_DOCUMENT_MARK (sysprof_document_mark_get_type()) +#define SYSPROF_IS_DOCUMENT_MARK(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_DOCUMENT_MARK) +#define SYSPROF_DOCUMENT_MARK(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_DOCUMENT_MARK, SysprofDocumentMark) +#define SYSPROF_DOCUMENT_MARK_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_DOCUMENT_MARK, SysprofDocumentMarkClass) + +typedef struct _SysprofDocumentMark SysprofDocumentMark; +typedef struct _SysprofDocumentMarkClass SysprofDocumentMarkClass; + +SYSPROF_AVAILABLE_IN_ALL +GType sysprof_document_mark_get_type (void) G_GNUC_CONST; +SYSPROF_AVAILABLE_IN_ALL +gint64 sysprof_document_mark_get_duration (SysprofDocumentMark *self); +SYSPROF_AVAILABLE_IN_ALL +const char *sysprof_document_mark_get_group (SysprofDocumentMark *self); +SYSPROF_AVAILABLE_IN_ALL +const char *sysprof_document_mark_get_name (SysprofDocumentMark *self); +SYSPROF_AVAILABLE_IN_ALL +const char *sysprof_document_mark_get_message (SysprofDocumentMark *self); + +G_END_DECLS + From 24d0d4af52009cb6b1640e8202783bc7a05617e8 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 27 Apr 2023 17:45:04 -0700 Subject: [PATCH 0022/1030] libsysprof-analyze: add document type for processes Generally this is all the processes at startup, but can also be a process that is executed while the capture is running. Useful to pair with an Exit frame for pid/tid duration. --- src/libsysprof-analyze/meson.build | 2 + src/libsysprof-analyze/sysprof-analyze.h | 1 + .../sysprof-document-frame.c | 3 + .../sysprof-document-process.c | 95 +++++++++++++++++++ .../sysprof-document-process.h | 41 ++++++++ 5 files changed, 142 insertions(+) create mode 100644 src/libsysprof-analyze/sysprof-document-process.c create mode 100644 src/libsysprof-analyze/sysprof-document-process.h diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build index 471a9ef3..7e13118d 100644 --- a/src/libsysprof-analyze/meson.build +++ b/src/libsysprof-analyze/meson.build @@ -4,6 +4,7 @@ libsysprof_analyze_public_sources = [ 'sysprof-document-log.c', 'sysprof-document-mark.c', 'sysprof-document-mmap.c', + 'sysprof-document-process.c', 'sysprof-document-sample.c', ] @@ -14,6 +15,7 @@ libsysprof_analyze_public_headers = [ 'sysprof-document-log.h', 'sysprof-document-mark.h', 'sysprof-document-mmap.h', + 'sysprof-document-process.h', 'sysprof-document-sample.h', ] diff --git a/src/libsysprof-analyze/sysprof-analyze.h b/src/libsysprof-analyze/sysprof-analyze.h index 01814bd4..d41763d9 100644 --- a/src/libsysprof-analyze/sysprof-analyze.h +++ b/src/libsysprof-analyze/sysprof-analyze.h @@ -30,6 +30,7 @@ G_BEGIN_DECLS # include "sysprof-document-log.h" # include "sysprof-document-mark.h" # include "sysprof-document-mmap.h" +# include "sysprof-document-process.h" # include "sysprof-document-sample.h" #undef SYSPROF_ANALYZE_INSIDE diff --git a/src/libsysprof-analyze/sysprof-document-frame.c b/src/libsysprof-analyze/sysprof-document-frame.c index 0f3fd8e6..daa6605c 100644 --- a/src/libsysprof-analyze/sysprof-document-frame.c +++ b/src/libsysprof-analyze/sysprof-document-frame.c @@ -24,6 +24,7 @@ #include "sysprof-document-log.h" #include "sysprof-document-mark.h" #include "sysprof-document-mmap.h" +#include "sysprof-document-process.h" #include "sysprof-document-sample.h" G_DEFINE_TYPE (SysprofDocumentFrame, sysprof_document_frame, G_TYPE_OBJECT) @@ -133,6 +134,8 @@ _sysprof_document_frame_new (GMappedFile *mapped_file, gtype = SYSPROF_TYPE_DOCUMENT_LOG; else if (frame->type == SYSPROF_CAPTURE_FRAME_MARK) gtype = SYSPROF_TYPE_DOCUMENT_MARK; + else if (frame->type == SYSPROF_CAPTURE_FRAME_PROCESS) + gtype = SYSPROF_TYPE_DOCUMENT_PROCESS; self = g_object_new (gtype, NULL); self->mapped_file = g_mapped_file_ref (mapped_file); diff --git a/src/libsysprof-analyze/sysprof-document-process.c b/src/libsysprof-analyze/sysprof-document-process.c new file mode 100644 index 00000000..d307616b --- /dev/null +++ b/src/libsysprof-analyze/sysprof-document-process.c @@ -0,0 +1,95 @@ +/* sysprof-document-process.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-document-frame-private.h" +#include "sysprof-document-process.h" + +struct _SysprofDocumentProcess +{ + SysprofDocumentFrame parent_instance; +}; + +struct _SysprofDocumentProcessClass +{ + SysprofDocumentFrameClass parent_class; +}; + +enum { + PROP_0, + PROP_COMMAND_LINE, + N_PROPS +}; + +G_DEFINE_FINAL_TYPE (SysprofDocumentProcess, sysprof_document_process, SYSPROF_TYPE_DOCUMENT_FRAME) + +static GParamSpec *properties [N_PROPS]; + +static void +sysprof_document_process_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofDocumentProcess *self = SYSPROF_DOCUMENT_PROCESS (object); + + switch (prop_id) + { + case PROP_COMMAND_LINE: + g_value_set_string (value, sysprof_document_process_get_command_line (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_document_process_class_init (SysprofDocumentProcessClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->get_property = sysprof_document_process_get_property; + + properties [PROP_COMMAND_LINE] = + g_param_spec_string ("command-line", NULL, NULL, + NULL, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); +} + +static void +sysprof_document_process_init (SysprofDocumentProcess *self) +{ +} + +const char * +sysprof_document_process_get_command_line (SysprofDocumentProcess *self) +{ + const SysprofCaptureProcess *proc; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_PROCESS (self), 0); + + proc = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureProcess); + + return SYSPROF_DOCUMENT_FRAME_CSTRING (self, proc->cmdline); +} diff --git a/src/libsysprof-analyze/sysprof-document-process.h b/src/libsysprof-analyze/sysprof-document-process.h new file mode 100644 index 00000000..c6da18db --- /dev/null +++ b/src/libsysprof-analyze/sysprof-document-process.h @@ -0,0 +1,41 @@ +/* sysprof-document-process.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-document-frame.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_DOCUMENT_PROCESS (sysprof_document_process_get_type()) +#define SYSPROF_IS_DOCUMENT_PROCESS(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_DOCUMENT_PROCESS) +#define SYSPROF_DOCUMENT_PROCESS(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_DOCUMENT_PROCESS, SysprofDocumentProcess) +#define SYSPROF_DOCUMENT_PROCESS_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_DOCUMENT_PROCESS, SysprofDocumentProcessClass) + +typedef struct _SysprofDocumentProcess SysprofDocumentProcess; +typedef struct _SysprofDocumentProcessClass SysprofDocumentProcessClass; + +SYSPROF_AVAILABLE_IN_ALL +GType sysprof_document_process_get_type (void) G_GNUC_CONST; +SYSPROF_AVAILABLE_IN_ALL +const char *sysprof_document_process_get_command_line (SysprofDocumentProcess *self); + +G_END_DECLS + From 89f9bba8e26243f77c69c207a6e9eed7deb99c0e Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 27 Apr 2023 17:45:32 -0700 Subject: [PATCH 0023/1030] libsysprof-analyze: annotate some frames with extra data --- .../tests/test-capture-model.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/libsysprof-analyze/tests/test-capture-model.c b/src/libsysprof-analyze/tests/test-capture-model.c index 8fb3b1ad..03ca08ee 100644 --- a/src/libsysprof-analyze/tests/test-capture-model.c +++ b/src/libsysprof-analyze/tests/test-capture-model.c @@ -36,12 +36,27 @@ main (int argc, { SysprofDocumentFrame *frame = g_list_model_get_item (G_LIST_MODEL (document), i); - g_print ("%"G_GINT64_FORMAT" [pid %d] [cpu %d] (type %s)\n", + g_print ("%"G_GINT64_FORMAT" [pid %d] [cpu %d] (type %s)", sysprof_document_frame_get_time (frame), sysprof_document_frame_get_pid (frame), sysprof_document_frame_get_cpu (frame), G_OBJECT_TYPE_NAME (frame)); + if (0) {} + else if (SYSPROF_IS_DOCUMENT_LOG (frame)) + g_print (" domain=%s message=%s", + sysprof_document_log_get_domain (SYSPROF_DOCUMENT_LOG (frame)), + sysprof_document_log_get_message (SYSPROF_DOCUMENT_LOG (frame))); + else if (SYSPROF_IS_DOCUMENT_MARK (frame)) + g_print (" group=%s name=%s message=%s", + sysprof_document_mark_get_group (SYSPROF_DOCUMENT_MARK (frame)), + sysprof_document_mark_get_name (SYSPROF_DOCUMENT_MARK (frame)), + sysprof_document_mark_get_message (SYSPROF_DOCUMENT_MARK (frame))); + else if (SYSPROF_IS_DOCUMENT_PROCESS (frame)) + g_print (" cmdline=%s", sysprof_document_process_get_command_line (SYSPROF_DOCUMENT_PROCESS (frame))); + + g_print ("\n"); + g_clear_object (&frame); } From d878fbf372bbe2f69158aa35fb62aff0e544f46b Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 27 Apr 2023 17:50:28 -0700 Subject: [PATCH 0024/1030] libsysprof-analyze: add document type for task exit We don't have any extra data here currently, but it can be nice to be able to check a gtype when consuming frames. --- src/libsysprof-analyze/meson.build | 2 + src/libsysprof-analyze/sysprof-analyze.h | 1 + .../sysprof-document-exit.c | 46 +++++++++++++++++++ .../sysprof-document-exit.h | 39 ++++++++++++++++ .../sysprof-document-frame.c | 4 ++ 5 files changed, 92 insertions(+) create mode 100644 src/libsysprof-analyze/sysprof-document-exit.c create mode 100644 src/libsysprof-analyze/sysprof-document-exit.h diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build index 7e13118d..edf80506 100644 --- a/src/libsysprof-analyze/meson.build +++ b/src/libsysprof-analyze/meson.build @@ -1,5 +1,6 @@ libsysprof_analyze_public_sources = [ 'sysprof-document.c', + 'sysprof-document-exit.c', 'sysprof-document-frame.c', 'sysprof-document-log.c', 'sysprof-document-mark.c', @@ -11,6 +12,7 @@ libsysprof_analyze_public_sources = [ libsysprof_analyze_public_headers = [ 'sysprof-analyze.h', 'sysprof-document.h', + 'sysprof-document-exit.h', 'sysprof-document-frame.h', 'sysprof-document-log.h', 'sysprof-document-mark.h', diff --git a/src/libsysprof-analyze/sysprof-analyze.h b/src/libsysprof-analyze/sysprof-analyze.h index d41763d9..73dfeeb2 100644 --- a/src/libsysprof-analyze/sysprof-analyze.h +++ b/src/libsysprof-analyze/sysprof-analyze.h @@ -26,6 +26,7 @@ G_BEGIN_DECLS #define SYSPROF_ANALYZE_INSIDE # include "sysprof-document.h" +# include "sysprof-document-exit.h" # include "sysprof-document-frame.h" # include "sysprof-document-log.h" # include "sysprof-document-mark.h" diff --git a/src/libsysprof-analyze/sysprof-document-exit.c b/src/libsysprof-analyze/sysprof-document-exit.c new file mode 100644 index 00000000..4e83cbab --- /dev/null +++ b/src/libsysprof-analyze/sysprof-document-exit.c @@ -0,0 +1,46 @@ +/* sysprof-document-exit.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-document-frame-private.h" +#include "sysprof-document-exit.h" + +struct _SysprofDocumentExit +{ + SysprofDocumentFrame parent_instance; +}; + +struct _SysprofDocumentExitClass +{ + SysprofDocumentFrameClass parent_class; +}; + +G_DEFINE_FINAL_TYPE (SysprofDocumentExit, sysprof_document_exit, SYSPROF_TYPE_DOCUMENT_FRAME) + +static void +sysprof_document_exit_class_init (SysprofDocumentExitClass *klass) +{ +} + +static void +sysprof_document_exit_init (SysprofDocumentExit *self) +{ +} diff --git a/src/libsysprof-analyze/sysprof-document-exit.h b/src/libsysprof-analyze/sysprof-document-exit.h new file mode 100644 index 00000000..3c902cf2 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-document-exit.h @@ -0,0 +1,39 @@ +/* sysprof-document-exit.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-document-frame.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_DOCUMENT_EXIT (sysprof_document_exit_get_type()) +#define SYSPROF_IS_DOCUMENT_EXIT(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_DOCUMENT_EXIT) +#define SYSPROF_DOCUMENT_EXIT(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_DOCUMENT_EXIT, SysprofDocumentExit) +#define SYSPROF_DOCUMENT_EXIT_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_DOCUMENT_EXIT, SysprofDocumentExitClass) + +typedef struct _SysprofDocumentExit SysprofDocumentExit; +typedef struct _SysprofDocumentExitClass SysprofDocumentExitClass; + +SYSPROF_AVAILABLE_IN_ALL +GType sysprof_document_exit_get_type (void) G_GNUC_CONST; + +G_END_DECLS + diff --git a/src/libsysprof-analyze/sysprof-document-frame.c b/src/libsysprof-analyze/sysprof-document-frame.c index daa6605c..0b13a3dd 100644 --- a/src/libsysprof-analyze/sysprof-document-frame.c +++ b/src/libsysprof-analyze/sysprof-document-frame.c @@ -21,6 +21,8 @@ #include "config.h" #include "sysprof-document-frame-private.h" + +#include "sysprof-document-exit.h" #include "sysprof-document-log.h" #include "sysprof-document-mark.h" #include "sysprof-document-mmap.h" @@ -136,6 +138,8 @@ _sysprof_document_frame_new (GMappedFile *mapped_file, gtype = SYSPROF_TYPE_DOCUMENT_MARK; else if (frame->type == SYSPROF_CAPTURE_FRAME_PROCESS) gtype = SYSPROF_TYPE_DOCUMENT_PROCESS; + else if (frame->type == SYSPROF_CAPTURE_FRAME_EXIT) + gtype = SYSPROF_TYPE_DOCUMENT_EXIT; self = g_object_new (gtype, NULL); self->mapped_file = g_mapped_file_ref (mapped_file); From 72c0ba731c88c8ce0b576ff4adae826617202b9b Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 27 Apr 2023 17:58:23 -0700 Subject: [PATCH 0025/1030] libsysprof-analyze: add document type for metadata --- src/libsysprof-analyze/meson.build | 2 + src/libsysprof-analyze/sysprof-analyze.h | 1 + .../sysprof-document-metadata.c | 117 ++++++++++++++++++ .../sysprof-document-metadata.h | 43 +++++++ .../tests/test-capture-model.c | 4 + 5 files changed, 167 insertions(+) create mode 100644 src/libsysprof-analyze/sysprof-document-metadata.c create mode 100644 src/libsysprof-analyze/sysprof-document-metadata.h diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build index edf80506..e3c2f237 100644 --- a/src/libsysprof-analyze/meson.build +++ b/src/libsysprof-analyze/meson.build @@ -4,6 +4,7 @@ libsysprof_analyze_public_sources = [ 'sysprof-document-frame.c', 'sysprof-document-log.c', 'sysprof-document-mark.c', + 'sysprof-document-metadata.c', 'sysprof-document-mmap.c', 'sysprof-document-process.c', 'sysprof-document-sample.c', @@ -16,6 +17,7 @@ libsysprof_analyze_public_headers = [ 'sysprof-document-frame.h', 'sysprof-document-log.h', 'sysprof-document-mark.h', + 'sysprof-document-metadata.h', 'sysprof-document-mmap.h', 'sysprof-document-process.h', 'sysprof-document-sample.h', diff --git a/src/libsysprof-analyze/sysprof-analyze.h b/src/libsysprof-analyze/sysprof-analyze.h index 73dfeeb2..e5b84129 100644 --- a/src/libsysprof-analyze/sysprof-analyze.h +++ b/src/libsysprof-analyze/sysprof-analyze.h @@ -30,6 +30,7 @@ G_BEGIN_DECLS # include "sysprof-document-frame.h" # include "sysprof-document-log.h" # include "sysprof-document-mark.h" +# include "sysprof-document-metadata.h" # include "sysprof-document-mmap.h" # include "sysprof-document-process.h" # include "sysprof-document-sample.h" diff --git a/src/libsysprof-analyze/sysprof-document-metadata.c b/src/libsysprof-analyze/sysprof-document-metadata.c new file mode 100644 index 00000000..f2e5905f --- /dev/null +++ b/src/libsysprof-analyze/sysprof-document-metadata.c @@ -0,0 +1,117 @@ +/* sysprof-document-metadata.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-document-frame-private.h" +#include "sysprof-document-metadata.h" + +struct _SysprofDocumentMetadata +{ + SysprofDocumentFrame parent_instance; +}; + +struct _SysprofDocumentMetadataClass +{ + SysprofDocumentFrameClass parent_class; +}; + +enum { + PROP_0, + PROP_ID, + PROP_VALUE, + N_PROPS +}; + +G_DEFINE_FINAL_TYPE (SysprofDocumentMetadata, sysprof_document_metadata, SYSPROF_TYPE_DOCUMENT_FRAME) + +static GParamSpec *properties [N_PROPS]; + +static void +sysprof_document_metadata_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofDocumentMetadata *self = SYSPROF_DOCUMENT_METADATA (object); + + switch (prop_id) + { + case PROP_ID: + g_value_set_string (value, sysprof_document_metadata_get_id (self)); + break; + + case PROP_VALUE: + g_value_set_string (value, sysprof_document_metadata_get_value (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_document_metadata_class_init (SysprofDocumentMetadataClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->get_property = sysprof_document_metadata_get_property; + + properties [PROP_ID] = + g_param_spec_string ("id", NULL, NULL, + NULL, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + properties [PROP_VALUE] = + g_param_spec_string ("value", NULL, NULL, + NULL, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); +} + +static void +sysprof_document_metadata_init (SysprofDocumentMetadata *self) +{ +} + +const char * +sysprof_document_metadata_get_id (SysprofDocumentMetadata *self) +{ + const SysprofCaptureMetadata *meta; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_METADATA (self), 0); + + meta = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureMetadata); + + return SYSPROF_DOCUMENT_FRAME_CSTRING (self, meta->id); +} + +const char * +sysprof_document_metadata_get_value (SysprofDocumentMetadata *self) +{ + const SysprofCaptureMetadata *meta; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_METADATA (self), 0); + + meta = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureMetadata); + + return SYSPROF_DOCUMENT_FRAME_CSTRING (self, meta->metadata); +} diff --git a/src/libsysprof-analyze/sysprof-document-metadata.h b/src/libsysprof-analyze/sysprof-document-metadata.h new file mode 100644 index 00000000..f32bbe8c --- /dev/null +++ b/src/libsysprof-analyze/sysprof-document-metadata.h @@ -0,0 +1,43 @@ +/* sysprof-document-metadata.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-document-frame.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_DOCUMENT_METADATA (sysprof_document_metadata_get_type()) +#define SYSPROF_IS_DOCUMENT_METADATA(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_DOCUMENT_METADATA) +#define SYSPROF_DOCUMENT_METADATA(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_DOCUMENT_METADATA, SysprofDocumentMetadata) +#define SYSPROF_DOCUMENT_METADATA_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_DOCUMENT_METADATA, SysprofDocumentMetadataClass) + +typedef struct _SysprofDocumentMetadata SysprofDocumentMetadata; +typedef struct _SysprofDocumentMetadataClass SysprofDocumentMetadataClass; + +SYSPROF_AVAILABLE_IN_ALL +GType sysprof_document_metadata_get_type (void) G_GNUC_CONST; +SYSPROF_AVAILABLE_IN_ALL +const char *sysprof_document_metadata_get_id (SysprofDocumentMetadata *self); +SYSPROF_AVAILABLE_IN_ALL +const char *sysprof_document_metadata_get_value (SysprofDocumentMetadata *self); + +G_END_DECLS + diff --git a/src/libsysprof-analyze/tests/test-capture-model.c b/src/libsysprof-analyze/tests/test-capture-model.c index 03ca08ee..35485924 100644 --- a/src/libsysprof-analyze/tests/test-capture-model.c +++ b/src/libsysprof-analyze/tests/test-capture-model.c @@ -54,6 +54,10 @@ main (int argc, sysprof_document_mark_get_message (SYSPROF_DOCUMENT_MARK (frame))); else if (SYSPROF_IS_DOCUMENT_PROCESS (frame)) g_print (" cmdline=%s", sysprof_document_process_get_command_line (SYSPROF_DOCUMENT_PROCESS (frame))); + else if (SYSPROF_IS_DOCUMENT_METADATA (frame)) + g_print (" id=%s value=%s", + sysprof_document_metadata_get_id (SYSPROF_DOCUMENT_METADATA (frame)), + sysprof_document_metadata_get_value (SYSPROF_DOCUMENT_METADATA (frame))); g_print ("\n"); From 6b762ef64a28dc77cc35bbf3cd49df099ca7734c Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 28 Apr 2023 10:36:17 -0700 Subject: [PATCH 0026/1030] libsysprof-analyze: add document type for fork --- src/libsysprof-analyze/meson.build | 2 + src/libsysprof-analyze/sysprof-analyze.h | 1 + .../sysprof-document-fork.c | 95 +++++++++++++++++++ .../sysprof-document-fork.h | 41 ++++++++ .../sysprof-document-frame.c | 50 +++++++--- .../tests/test-capture-model.c | 3 + 6 files changed, 178 insertions(+), 14 deletions(-) create mode 100644 src/libsysprof-analyze/sysprof-document-fork.c create mode 100644 src/libsysprof-analyze/sysprof-document-fork.h diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build index e3c2f237..1c0f0eb2 100644 --- a/src/libsysprof-analyze/meson.build +++ b/src/libsysprof-analyze/meson.build @@ -1,6 +1,7 @@ libsysprof_analyze_public_sources = [ 'sysprof-document.c', 'sysprof-document-exit.c', + 'sysprof-document-fork.c', 'sysprof-document-frame.c', 'sysprof-document-log.c', 'sysprof-document-mark.c', @@ -14,6 +15,7 @@ libsysprof_analyze_public_headers = [ 'sysprof-analyze.h', 'sysprof-document.h', 'sysprof-document-exit.h', + 'sysprof-document-fork.h', 'sysprof-document-frame.h', 'sysprof-document-log.h', 'sysprof-document-mark.h', diff --git a/src/libsysprof-analyze/sysprof-analyze.h b/src/libsysprof-analyze/sysprof-analyze.h index e5b84129..c4d6df45 100644 --- a/src/libsysprof-analyze/sysprof-analyze.h +++ b/src/libsysprof-analyze/sysprof-analyze.h @@ -27,6 +27,7 @@ G_BEGIN_DECLS #define SYSPROF_ANALYZE_INSIDE # include "sysprof-document.h" # include "sysprof-document-exit.h" +# include "sysprof-document-fork.h" # include "sysprof-document-frame.h" # include "sysprof-document-log.h" # include "sysprof-document-mark.h" diff --git a/src/libsysprof-analyze/sysprof-document-fork.c b/src/libsysprof-analyze/sysprof-document-fork.c new file mode 100644 index 00000000..fd4c44bf --- /dev/null +++ b/src/libsysprof-analyze/sysprof-document-fork.c @@ -0,0 +1,95 @@ +/* sysprof-document-fork.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-document-frame-private.h" +#include "sysprof-document-fork.h" + +struct _SysprofDocumentFork +{ + SysprofDocumentFrame parent_instance; +}; + +struct _SysprofDocumentForkClass +{ + SysprofDocumentFrameClass parent_class; +}; + +enum { + PROP_0, + PROP_CHILD_PID, + N_PROPS +}; + +G_DEFINE_FINAL_TYPE (SysprofDocumentFork, sysprof_document_fork, SYSPROF_TYPE_DOCUMENT_FRAME) + +static GParamSpec *properties [N_PROPS]; + +static void +sysprof_document_fork_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofDocumentFork *self = SYSPROF_DOCUMENT_FORK (object); + + switch (prop_id) + { + case PROP_CHILD_PID: + g_value_set_int (value, sysprof_document_fork_get_child_pid (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_document_fork_class_init (SysprofDocumentForkClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->get_property = sysprof_document_fork_get_property; + + properties [PROP_CHILD_PID] = + g_param_spec_int ("child-pid", NULL, NULL, + G_MININT, G_MAXINT, 0, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); +} + +static void +sysprof_document_fork_init (SysprofDocumentFork *self) +{ +} + +int +sysprof_document_fork_get_child_pid (SysprofDocumentFork *self) +{ + const SysprofCaptureFork *fork; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_FORK (self), 0); + + fork = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureFork); + + return SYSPROF_DOCUMENT_FRAME_INT32 (self, fork->child_pid); +} diff --git a/src/libsysprof-analyze/sysprof-document-fork.h b/src/libsysprof-analyze/sysprof-document-fork.h new file mode 100644 index 00000000..021a157e --- /dev/null +++ b/src/libsysprof-analyze/sysprof-document-fork.h @@ -0,0 +1,41 @@ +/* sysprof-document-fork.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-document-frame.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_DOCUMENT_FORK (sysprof_document_fork_get_type()) +#define SYSPROF_IS_DOCUMENT_FORK(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_DOCUMENT_FORK) +#define SYSPROF_DOCUMENT_FORK(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_DOCUMENT_FORK, SysprofDocumentFork) +#define SYSPROF_DOCUMENT_FORK_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_DOCUMENT_FORK, SysprofDocumentForkClass) + +typedef struct _SysprofDocumentFork SysprofDocumentFork; +typedef struct _SysprofDocumentForkClass SysprofDocumentForkClass; + +SYSPROF_AVAILABLE_IN_ALL +GType sysprof_document_fork_get_type (void) G_GNUC_CONST; +SYSPROF_AVAILABLE_IN_ALL +int sysprof_document_fork_get_child_pid (SysprofDocumentFork *self); + +G_END_DECLS + diff --git a/src/libsysprof-analyze/sysprof-document-frame.c b/src/libsysprof-analyze/sysprof-document-frame.c index 0b13a3dd..e96ead77 100644 --- a/src/libsysprof-analyze/sysprof-document-frame.c +++ b/src/libsysprof-analyze/sysprof-document-frame.c @@ -23,6 +23,7 @@ #include "sysprof-document-frame-private.h" #include "sysprof-document-exit.h" +#include "sysprof-document-fork.h" #include "sysprof-document-log.h" #include "sysprof-document-mark.h" #include "sysprof-document-mmap.h" @@ -124,22 +125,43 @@ _sysprof_document_frame_new (GMappedFile *mapped_file, guint16 frame_len, gboolean needs_swap) { - GType gtype = SYSPROF_TYPE_DOCUMENT_FRAME; SysprofDocumentFrame *self; + GType gtype; - if (0) {} - else if (frame->type == SYSPROF_CAPTURE_FRAME_SAMPLE) - gtype = SYSPROF_TYPE_DOCUMENT_SAMPLE; - else if (frame->type == SYSPROF_CAPTURE_FRAME_MAP) - gtype = SYSPROF_TYPE_DOCUMENT_MMAP; - else if (frame->type == SYSPROF_CAPTURE_FRAME_LOG) - gtype = SYSPROF_TYPE_DOCUMENT_LOG; - else if (frame->type == SYSPROF_CAPTURE_FRAME_MARK) - gtype = SYSPROF_TYPE_DOCUMENT_MARK; - else if (frame->type == SYSPROF_CAPTURE_FRAME_PROCESS) - gtype = SYSPROF_TYPE_DOCUMENT_PROCESS; - else if (frame->type == SYSPROF_CAPTURE_FRAME_EXIT) - gtype = SYSPROF_TYPE_DOCUMENT_EXIT; + switch (frame->type) + { + case SYSPROF_CAPTURE_FRAME_SAMPLE: + gtype = SYSPROF_TYPE_DOCUMENT_SAMPLE; + break; + + case SYSPROF_CAPTURE_FRAME_MAP: + gtype = SYSPROF_TYPE_DOCUMENT_MMAP; + break; + + case SYSPROF_CAPTURE_FRAME_LOG: + gtype = SYSPROF_TYPE_DOCUMENT_LOG; + break; + + case SYSPROF_CAPTURE_FRAME_MARK: + gtype = SYSPROF_TYPE_DOCUMENT_MARK; + break; + + case SYSPROF_CAPTURE_FRAME_PROCESS: + gtype = SYSPROF_TYPE_DOCUMENT_PROCESS; + break; + + case SYSPROF_CAPTURE_FRAME_EXIT: + gtype = SYSPROF_TYPE_DOCUMENT_EXIT; + break; + + case SYSPROF_CAPTURE_FRAME_FORK: + gtype = SYSPROF_TYPE_DOCUMENT_FORK; + break; + + default: + gtype = SYSPROF_TYPE_DOCUMENT_FRAME; + break; + } self = g_object_new (gtype, NULL); self->mapped_file = g_mapped_file_ref (mapped_file); diff --git a/src/libsysprof-analyze/tests/test-capture-model.c b/src/libsysprof-analyze/tests/test-capture-model.c index 35485924..6044a49c 100644 --- a/src/libsysprof-analyze/tests/test-capture-model.c +++ b/src/libsysprof-analyze/tests/test-capture-model.c @@ -58,6 +58,9 @@ main (int argc, g_print (" id=%s value=%s", sysprof_document_metadata_get_id (SYSPROF_DOCUMENT_METADATA (frame)), sysprof_document_metadata_get_value (SYSPROF_DOCUMENT_METADATA (frame))); + else if (SYSPROF_IS_DOCUMENT_FORK (frame)) + g_print (" child-pid=%d", + sysprof_document_fork_get_child_pid (SYSPROF_DOCUMENT_FORK (frame))); g_print ("\n"); From 86561c0c4f0c12ee3b830b5d5da35775012b5b57 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 28 Apr 2023 11:44:30 -0700 Subject: [PATCH 0027/1030] libyssprof-analyze: add document type for allocations --- src/libsysprof-analyze/meson.build | 2 + src/libsysprof-analyze/sysprof-analyze.h | 1 + .../sysprof-document-allocation.c | 172 ++++++++++++++++++ .../sysprof-document-allocation.h | 46 +++++ .../sysprof-document-frame.c | 5 + .../tests/test-capture-model.c | 10 + 6 files changed, 236 insertions(+) create mode 100644 src/libsysprof-analyze/sysprof-document-allocation.c create mode 100644 src/libsysprof-analyze/sysprof-document-allocation.h diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build index 1c0f0eb2..f41e04bb 100644 --- a/src/libsysprof-analyze/meson.build +++ b/src/libsysprof-analyze/meson.build @@ -1,5 +1,6 @@ libsysprof_analyze_public_sources = [ 'sysprof-document.c', + 'sysprof-document-allocation.c', 'sysprof-document-exit.c', 'sysprof-document-fork.c', 'sysprof-document-frame.c', @@ -14,6 +15,7 @@ libsysprof_analyze_public_sources = [ libsysprof_analyze_public_headers = [ 'sysprof-analyze.h', 'sysprof-document.h', + 'sysprof-document-allocation.h', 'sysprof-document-exit.h', 'sysprof-document-fork.h', 'sysprof-document-frame.h', diff --git a/src/libsysprof-analyze/sysprof-analyze.h b/src/libsysprof-analyze/sysprof-analyze.h index c4d6df45..47dc4193 100644 --- a/src/libsysprof-analyze/sysprof-analyze.h +++ b/src/libsysprof-analyze/sysprof-analyze.h @@ -26,6 +26,7 @@ G_BEGIN_DECLS #define SYSPROF_ANALYZE_INSIDE # include "sysprof-document.h" +# include "sysprof-document-allocation.h" # include "sysprof-document-exit.h" # include "sysprof-document-fork.h" # include "sysprof-document-frame.h" diff --git a/src/libsysprof-analyze/sysprof-document-allocation.c b/src/libsysprof-analyze/sysprof-document-allocation.c new file mode 100644 index 00000000..da7a73af --- /dev/null +++ b/src/libsysprof-analyze/sysprof-document-allocation.c @@ -0,0 +1,172 @@ +/* sysprof-document-allocation.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-document-frame-private.h" +#include "sysprof-document-allocation.h" + +struct _SysprofDocumentAllocation +{ + SysprofDocumentFrame parent_instance; +}; + +struct _SysprofDocumentAllocationClass +{ + SysprofDocumentFrameClass parent_class; +}; + +enum { + PROP_0, + PROP_ADDRESS, + PROP_SIZE, + PROP_TID, + N_PROPS +}; + +G_DEFINE_FINAL_TYPE (SysprofDocumentAllocation, sysprof_document_allocation, SYSPROF_TYPE_DOCUMENT_FRAME) + +static GParamSpec *properties [N_PROPS]; + +static void +sysprof_document_allocation_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofDocumentAllocation *self = SYSPROF_DOCUMENT_ALLOCATION (object); + + switch (prop_id) + { + case PROP_ADDRESS: + g_value_set_uint64 (value, sysprof_document_allocation_get_address (self)); + break; + + case PROP_SIZE: + g_value_set_int64 (value, sysprof_document_allocation_get_size (self)); + break; + + case PROP_TID: + g_value_set_int (value, sysprof_document_allocation_get_tid (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_document_allocation_class_init (SysprofDocumentAllocationClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->get_property = sysprof_document_allocation_get_property; + + /** + * SysprofDocumentAllocation:tid: + * + * The task-id or thread-id of the thread which was sampled. + * + * On Linux, this is generally set to the value of the gettid() syscall. + * + * Since: 45 + */ + properties [PROP_TID] = + g_param_spec_int ("tid", NULL, NULL, + G_MININT32, G_MAXINT32, 0, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + /** + * SysprofDocumentAllocation:address: + * + * The address that was allocated or freed. + * + * Since: 45 + */ + properties [PROP_ADDRESS] = + g_param_spec_uint64 ("address", NULL, NULL, + 0, G_MAXUINT64, 0, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + /** + * SysprofDocumentAllocation:size: + * + * The size of the memory that was allocated or freed. + */ + properties [PROP_SIZE] = + g_param_spec_int64 ("size", NULL, NULL, + G_MININT64, G_MAXINT64, 0, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); +} + +static void +sysprof_document_allocation_init (SysprofDocumentAllocation *self) +{ +} + +guint64 +sysprof_document_allocation_get_address (SysprofDocumentAllocation *self) +{ + const SysprofCaptureAllocation *allocation; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_ALLOCATION (self), 0); + + allocation = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureAllocation); + + return SYSPROF_DOCUMENT_FRAME_UINT64 (self, allocation->alloc_addr); +} + +gint64 +sysprof_document_allocation_get_size (SysprofDocumentAllocation *self) +{ + const SysprofCaptureAllocation *allocation; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_ALLOCATION (self), 0); + + allocation = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureAllocation); + + return SYSPROF_DOCUMENT_FRAME_INT64 (self, allocation->alloc_size); +} + +int +sysprof_document_allocation_get_tid (SysprofDocumentAllocation *self) +{ + const SysprofCaptureAllocation *allocation; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_ALLOCATION (self), 0); + + allocation = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureAllocation); + + return SYSPROF_DOCUMENT_FRAME_INT32 (self, allocation->tid); +} + +gboolean +sysprof_document_allocation_is_free (SysprofDocumentAllocation *self) +{ + const SysprofCaptureAllocation *allocation; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_ALLOCATION (self), 0); + + allocation = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureAllocation); + + return allocation->alloc_size == 0; +} diff --git a/src/libsysprof-analyze/sysprof-document-allocation.h b/src/libsysprof-analyze/sysprof-document-allocation.h new file mode 100644 index 00000000..c8b91d0f --- /dev/null +++ b/src/libsysprof-analyze/sysprof-document-allocation.h @@ -0,0 +1,46 @@ +/* sysprof-document-allocation.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-document-frame.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_DOCUMENT_ALLOCATION (sysprof_document_allocation_get_type()) +#define SYSPROF_IS_DOCUMENT_ALLOCATION(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_DOCUMENT_ALLOCATION) +#define SYSPROF_DOCUMENT_ALLOCATION(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_DOCUMENT_ALLOCATION, SysprofDocumentAllocation) +#define SYSPROF_DOCUMENT_ALLOCATION_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_DOCUMENT_ALLOCATION, SysprofDocumentAllocationClass) + +typedef struct _SysprofDocumentAllocation SysprofDocumentAllocation; +typedef struct _SysprofDocumentAllocationClass SysprofDocumentAllocationClass; + +SYSPROF_AVAILABLE_IN_ALL +GType sysprof_document_allocation_get_type (void) G_GNUC_CONST; +SYSPROF_AVAILABLE_IN_ALL +guint64 sysprof_document_allocation_get_address (SysprofDocumentAllocation *self); +SYSPROF_AVAILABLE_IN_ALL +gint64 sysprof_document_allocation_get_size (SysprofDocumentAllocation *self); +SYSPROF_AVAILABLE_IN_ALL +int sysprof_document_allocation_get_tid (SysprofDocumentAllocation *self); +SYSPROF_AVAILABLE_IN_ALL +gboolean sysprof_document_allocation_is_free (SysprofDocumentAllocation *self); + +G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-document-frame.c b/src/libsysprof-analyze/sysprof-document-frame.c index e96ead77..d9374ae2 100644 --- a/src/libsysprof-analyze/sysprof-document-frame.c +++ b/src/libsysprof-analyze/sysprof-document-frame.c @@ -22,6 +22,7 @@ #include "sysprof-document-frame-private.h" +#include "sysprof-document-allocation.h" #include "sysprof-document-exit.h" #include "sysprof-document-fork.h" #include "sysprof-document-log.h" @@ -158,6 +159,10 @@ _sysprof_document_frame_new (GMappedFile *mapped_file, gtype = SYSPROF_TYPE_DOCUMENT_FORK; break; + case SYSPROF_CAPTURE_FRAME_ALLOCATION: + gtype = SYSPROF_TYPE_DOCUMENT_ALLOCATION; + break; + default: gtype = SYSPROF_TYPE_DOCUMENT_FRAME; break; diff --git a/src/libsysprof-analyze/tests/test-capture-model.c b/src/libsysprof-analyze/tests/test-capture-model.c index 6044a49c..04d36e13 100644 --- a/src/libsysprof-analyze/tests/test-capture-model.c +++ b/src/libsysprof-analyze/tests/test-capture-model.c @@ -61,6 +61,16 @@ main (int argc, else if (SYSPROF_IS_DOCUMENT_FORK (frame)) g_print (" child-pid=%d", sysprof_document_fork_get_child_pid (SYSPROF_DOCUMENT_FORK (frame))); + else if (SYSPROF_IS_DOCUMENT_ALLOCATION (frame)) + { + if (sysprof_document_allocation_is_free (SYSPROF_DOCUMENT_ALLOCATION (frame))) + g_print (" 0x%016"G_GINT64_MODIFIER"x: free", + sysprof_document_allocation_get_address (SYSPROF_DOCUMENT_ALLOCATION (frame))); + else + g_print (" 0x%016"G_GINT64_MODIFIER"x: allocate %"G_GUINT64_FORMAT" at ", + sysprof_document_allocation_get_address (SYSPROF_DOCUMENT_ALLOCATION (frame)), + sysprof_document_allocation_get_size (SYSPROF_DOCUMENT_ALLOCATION (frame))); + } g_print ("\n"); From 5efa3d04e7c4d5c690769b4f9f1c19cf64d90bf0 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 28 Apr 2023 11:46:12 -0700 Subject: [PATCH 0028/1030] libsysprof-analyze: add is-free convenience property This just allows filtering by the property rather than having to do an expression like size==0. --- src/libsysprof-analyze/sysprof-document-allocation.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-document-allocation.c b/src/libsysprof-analyze/sysprof-document-allocation.c index da7a73af..8a24238c 100644 --- a/src/libsysprof-analyze/sysprof-document-allocation.c +++ b/src/libsysprof-analyze/sysprof-document-allocation.c @@ -36,6 +36,7 @@ struct _SysprofDocumentAllocationClass enum { PROP_0, PROP_ADDRESS, + PROP_IS_FREE, PROP_SIZE, PROP_TID, N_PROPS @@ -59,6 +60,10 @@ sysprof_document_allocation_get_property (GObject *object, g_value_set_uint64 (value, sysprof_document_allocation_get_address (self)); break; + case PROP_IS_FREE: + g_value_set_boolean (value, sysprof_document_allocation_is_free (self)); + break; + case PROP_SIZE: g_value_set_int64 (value, sysprof_document_allocation_get_size (self)); break; @@ -115,6 +120,11 @@ sysprof_document_allocation_class_init (SysprofDocumentAllocationClass *klass) G_MININT64, G_MAXINT64, 0, (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + properties [PROP_IS_FREE] = + g_param_spec_boolean ("is-free", NULL, NULL, + FALSE, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_properties (object_class, N_PROPS, properties); } From fdfe130edde53084d4a0dea2e5d58940e6b30e1b Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 28 Apr 2023 12:13:08 -0700 Subject: [PATCH 0029/1030] libsysprof-analyze: add missing autoptr cleanup funcs --- src/libsysprof-analyze/sysprof-document-allocation.h | 2 ++ src/libsysprof-analyze/sysprof-document-exit.h | 2 ++ src/libsysprof-analyze/sysprof-document-fork.h | 2 ++ src/libsysprof-analyze/sysprof-document-frame.h | 4 ++++ src/libsysprof-analyze/sysprof-document-log.h | 2 ++ src/libsysprof-analyze/sysprof-document-mark.h | 2 ++ src/libsysprof-analyze/sysprof-document-metadata.h | 2 ++ src/libsysprof-analyze/sysprof-document-mmap.h | 2 ++ src/libsysprof-analyze/sysprof-document-process.h | 2 ++ src/libsysprof-analyze/sysprof-document-sample.h | 2 ++ 10 files changed, 22 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-document-allocation.h b/src/libsysprof-analyze/sysprof-document-allocation.h index c8b91d0f..9744bf6b 100644 --- a/src/libsysprof-analyze/sysprof-document-allocation.h +++ b/src/libsysprof-analyze/sysprof-document-allocation.h @@ -43,4 +43,6 @@ int sysprof_document_allocation_get_tid (SysprofDocumentAllocation *sel SYSPROF_AVAILABLE_IN_ALL gboolean sysprof_document_allocation_is_free (SysprofDocumentAllocation *self); +G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofDocumentAllocation, g_object_unref) + G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-document-exit.h b/src/libsysprof-analyze/sysprof-document-exit.h index 3c902cf2..b9f351eb 100644 --- a/src/libsysprof-analyze/sysprof-document-exit.h +++ b/src/libsysprof-analyze/sysprof-document-exit.h @@ -35,5 +35,7 @@ typedef struct _SysprofDocumentExitClass SysprofDocumentExitClass; SYSPROF_AVAILABLE_IN_ALL GType sysprof_document_exit_get_type (void) G_GNUC_CONST; +G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofDocumentExit, g_object_unref) + G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-document-fork.h b/src/libsysprof-analyze/sysprof-document-fork.h index 021a157e..cd8caef0 100644 --- a/src/libsysprof-analyze/sysprof-document-fork.h +++ b/src/libsysprof-analyze/sysprof-document-fork.h @@ -37,5 +37,7 @@ GType sysprof_document_fork_get_type (void) G_GNUC_CONST; SYSPROF_AVAILABLE_IN_ALL int sysprof_document_fork_get_child_pid (SysprofDocumentFork *self); +G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofDocumentFork, g_object_unref) + G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-document-frame.h b/src/libsysprof-analyze/sysprof-document-frame.h index ffc31120..46d98055 100644 --- a/src/libsysprof-analyze/sysprof-document-frame.h +++ b/src/libsysprof-analyze/sysprof-document-frame.h @@ -22,6 +22,8 @@ #include +#include + G_BEGIN_DECLS #define SYSPROF_TYPE_DOCUMENT_FRAME (sysprof_document_frame_get_type()) @@ -41,4 +43,6 @@ int sysprof_document_frame_get_pid (SysprofDocumentFrame *self); SYSPROF_AVAILABLE_IN_ALL gint64 sysprof_document_frame_get_time (SysprofDocumentFrame *self); +G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofDocumentFrame, g_object_unref) + G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-document-log.h b/src/libsysprof-analyze/sysprof-document-log.h index 0cbf436d..191c9d91 100644 --- a/src/libsysprof-analyze/sysprof-document-log.h +++ b/src/libsysprof-analyze/sysprof-document-log.h @@ -41,4 +41,6 @@ GLogLevelFlags sysprof_document_log_get_severity (SysprofDocumentLog *self); SYSPROF_AVAILABLE_IN_ALL const char *sysprof_document_log_get_domain (SysprofDocumentLog *self); +G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofDocumentLog, g_object_unref) + G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-document-mark.h b/src/libsysprof-analyze/sysprof-document-mark.h index a875af30..99bd7550 100644 --- a/src/libsysprof-analyze/sysprof-document-mark.h +++ b/src/libsysprof-analyze/sysprof-document-mark.h @@ -43,5 +43,7 @@ const char *sysprof_document_mark_get_name (SysprofDocumentMark *self); SYSPROF_AVAILABLE_IN_ALL const char *sysprof_document_mark_get_message (SysprofDocumentMark *self); +G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofDocumentMark, g_object_unref) + G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-document-metadata.h b/src/libsysprof-analyze/sysprof-document-metadata.h index f32bbe8c..de5672a7 100644 --- a/src/libsysprof-analyze/sysprof-document-metadata.h +++ b/src/libsysprof-analyze/sysprof-document-metadata.h @@ -39,5 +39,7 @@ const char *sysprof_document_metadata_get_id (SysprofDocumentMetadata *self); SYSPROF_AVAILABLE_IN_ALL const char *sysprof_document_metadata_get_value (SysprofDocumentMetadata *self); +G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofDocumentMetadata, g_object_unref) + G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-document-mmap.h b/src/libsysprof-analyze/sysprof-document-mmap.h index bdcbf870..0bbe3deb 100644 --- a/src/libsysprof-analyze/sysprof-document-mmap.h +++ b/src/libsysprof-analyze/sysprof-document-mmap.h @@ -46,4 +46,6 @@ guint64 sysprof_document_mmap_get_file_offset (SysprofDocumentMmap *self); SYSPROF_AVAILABLE_IN_ALL const char *sysprof_document_mmap_get_file (SysprofDocumentMmap *self); +G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofDocumentMmap, g_object_unref) + G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-document-process.h b/src/libsysprof-analyze/sysprof-document-process.h index c6da18db..5234eb7e 100644 --- a/src/libsysprof-analyze/sysprof-document-process.h +++ b/src/libsysprof-analyze/sysprof-document-process.h @@ -37,5 +37,7 @@ GType sysprof_document_process_get_type (void) G_GNUC_CONST; SYSPROF_AVAILABLE_IN_ALL const char *sysprof_document_process_get_command_line (SysprofDocumentProcess *self); +G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofDocumentProcess, g_object_unref) + G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-document-sample.h b/src/libsysprof-analyze/sysprof-document-sample.h index 9a8906da..6868fc84 100644 --- a/src/libsysprof-analyze/sysprof-document-sample.h +++ b/src/libsysprof-analyze/sysprof-document-sample.h @@ -39,4 +39,6 @@ guint sysprof_document_sample_get_depth (SysprofDocumentSample *self); SYSPROF_AVAILABLE_IN_ALL int sysprof_document_sample_get_tid (SysprofDocumentSample *self); +G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofDocumentSample, g_object_unref) + G_END_DECLS From fe4b6ee8124632fdb081f9566d60efb3ef132119 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 28 Apr 2023 12:14:17 -0700 Subject: [PATCH 0030/1030] libsysprof-analyze: add interface for stracetrace types Currently, this would need to be implemented by SysprofDocumentSample and SysprofDocumentAllocation. But we could potentially start allowing many types to attach a trace if we modifier the capture format to allow it. --- src/libsysprof-analyze/meson.build | 2 + src/libsysprof-analyze/sysprof-analyze.h | 1 + .../sysprof-document-traceable.c | 44 +++++++++++++++++ .../sysprof-document-traceable.h | 48 +++++++++++++++++++ 4 files changed, 95 insertions(+) create mode 100644 src/libsysprof-analyze/sysprof-document-traceable.c create mode 100644 src/libsysprof-analyze/sysprof-document-traceable.h diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build index f41e04bb..76388006 100644 --- a/src/libsysprof-analyze/meson.build +++ b/src/libsysprof-analyze/meson.build @@ -10,6 +10,7 @@ libsysprof_analyze_public_sources = [ 'sysprof-document-mmap.c', 'sysprof-document-process.c', 'sysprof-document-sample.c', + 'sysprof-document-traceable.c', ] libsysprof_analyze_public_headers = [ @@ -25,6 +26,7 @@ libsysprof_analyze_public_headers = [ 'sysprof-document-mmap.h', 'sysprof-document-process.h', 'sysprof-document-sample.h', + 'sysprof-document-traceable.h', ] libsysprof_analyze_deps = [ diff --git a/src/libsysprof-analyze/sysprof-analyze.h b/src/libsysprof-analyze/sysprof-analyze.h index 47dc4193..ed3f9bd3 100644 --- a/src/libsysprof-analyze/sysprof-analyze.h +++ b/src/libsysprof-analyze/sysprof-analyze.h @@ -36,6 +36,7 @@ G_BEGIN_DECLS # include "sysprof-document-mmap.h" # include "sysprof-document-process.h" # include "sysprof-document-sample.h" +# include "sysprof-document-traceable.h" #undef SYSPROF_ANALYZE_INSIDE G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-document-traceable.c b/src/libsysprof-analyze/sysprof-document-traceable.c new file mode 100644 index 00000000..2b6442c7 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-document-traceable.c @@ -0,0 +1,44 @@ +/* + * sysprof-document-traceable.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-document-traceable.h" + +G_DEFINE_INTERFACE (SysprofDocumentTraceable, sysprof_document_traceable, SYSPROF_TYPE_DOCUMENT_FRAME) + +static void +sysprof_document_traceable_default_init (SysprofDocumentTraceableInterface *iface) +{ +} + +guint +sysprof_document_traceable_get_stack_depth (SysprofDocumentTraceable *self) +{ + return SYSPROF_DOCUMENT_TRACEABLE_GET_IFACE (self)->get_stack_depth (self); +} + +guint64 +sysprof_document_traceable_get_stack_address (SysprofDocumentTraceable *self, + guint position) +{ + return SYSPROF_DOCUMENT_TRACEABLE_GET_IFACE (self)->get_stack_address (self, position); +} diff --git a/src/libsysprof-analyze/sysprof-document-traceable.h b/src/libsysprof-analyze/sysprof-document-traceable.h new file mode 100644 index 00000000..ad77117e --- /dev/null +++ b/src/libsysprof-analyze/sysprof-document-traceable.h @@ -0,0 +1,48 @@ +/* + * sysprof-document-traceable.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-document-frame.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_DOCUMENT_TRACEABLE (sysprof_document_traceable_get_type ()) + +SYSPROF_AVAILABLE_IN_ALL +G_DECLARE_INTERFACE (SysprofDocumentTraceable, sysprof_document_traceable, SYSPROF, DOCUMENT_TRACEABLE, SysprofDocumentFrame) + +struct _SysprofDocumentTraceableInterface +{ + GTypeInterface parent; + + guint (*get_stack_depth) (SysprofDocumentTraceable *self); + guint64 (*get_stack_address) (SysprofDocumentTraceable *self, + guint position); +}; + +SYSPROF_AVAILABLE_IN_ALL +guint sysprof_document_traceable_get_stack_depth (SysprofDocumentTraceable *self); +SYSPROF_AVAILABLE_IN_ALL +guint64 sysprof_document_traceable_get_stack_address (SysprofDocumentTraceable *self, + guint position); + +G_END_DECLS From 98edb9c39a73b9959e5265b543d811125e5ccae9 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 28 Apr 2023 12:18:12 -0700 Subject: [PATCH 0031/1030] libsysprof-analyze: implement traceable for allocations --- .../sysprof-document-allocation.c | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/libsysprof-analyze/sysprof-document-allocation.c b/src/libsysprof-analyze/sysprof-document-allocation.c index 8a24238c..33314fb5 100644 --- a/src/libsysprof-analyze/sysprof-document-allocation.c +++ b/src/libsysprof-analyze/sysprof-document-allocation.c @@ -21,7 +21,9 @@ #include "config.h" #include "sysprof-document-frame-private.h" + #include "sysprof-document-allocation.h" +#include "sysprof-document-traceable.h" struct _SysprofDocumentAllocation { @@ -42,7 +44,32 @@ enum { N_PROPS }; -G_DEFINE_FINAL_TYPE (SysprofDocumentAllocation, sysprof_document_allocation, SYSPROF_TYPE_DOCUMENT_FRAME) +static guint +sysprof_document_allocation_get_stack_depth (SysprofDocumentTraceable *traceable) +{ + const SysprofCaptureAllocation *allocation = SYSPROF_DOCUMENT_FRAME_GET (traceable, SysprofCaptureAllocation); + + return SYSPROF_DOCUMENT_FRAME_UINT16 (traceable, allocation->n_addrs); +} + +static guint64 +sysprof_document_allocation_get_stack_address (SysprofDocumentTraceable *traceable, + guint position) +{ + const SysprofCaptureAllocation *allocation = SYSPROF_DOCUMENT_FRAME_GET (traceable, SysprofCaptureAllocation); + + return SYSPROF_DOCUMENT_FRAME_UINT64 (traceable, allocation->addrs[position]); +} + +static void +traceable_iface_init (SysprofDocumentTraceableInterface *iface) +{ + iface->get_stack_depth = sysprof_document_allocation_get_stack_depth; + iface->get_stack_address = sysprof_document_allocation_get_stack_address; +} + +G_DEFINE_FINAL_TYPE_WITH_CODE (SysprofDocumentAllocation, sysprof_document_allocation, SYSPROF_TYPE_DOCUMENT_FRAME, + G_IMPLEMENT_INTERFACE (SYSPROF_TYPE_DOCUMENT_TRACEABLE, traceable_iface_init)) static GParamSpec *properties [N_PROPS]; From 8c5b4720c0d5a87ea74c20dbae39896ca4570b56 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 28 Apr 2023 12:19:59 -0700 Subject: [PATCH 0032/1030] libsysprof-analyze: implement traceable for samples --- .../sysprof-document-sample.c | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/libsysprof-analyze/sysprof-document-sample.c b/src/libsysprof-analyze/sysprof-document-sample.c index 8fbbe2d4..a7c8fa23 100644 --- a/src/libsysprof-analyze/sysprof-document-sample.c +++ b/src/libsysprof-analyze/sysprof-document-sample.c @@ -21,7 +21,9 @@ #include "config.h" #include "sysprof-document-frame-private.h" + #include "sysprof-document-sample.h" +#include "sysprof-document-traceable.h" struct _SysprofDocumentSample { @@ -40,7 +42,32 @@ enum { N_PROPS }; -G_DEFINE_FINAL_TYPE (SysprofDocumentSample, sysprof_document_sample, SYSPROF_TYPE_DOCUMENT_FRAME) +static guint +sysprof_document_sample_get_stack_depth (SysprofDocumentTraceable *traceable) +{ + const SysprofCaptureSample *sample = SYSPROF_DOCUMENT_FRAME_GET (traceable, SysprofCaptureSample); + + return SYSPROF_DOCUMENT_FRAME_UINT16 (traceable, sample->n_addrs); +} + +static guint64 +sysprof_document_sample_get_stack_address (SysprofDocumentTraceable *traceable, + guint position) +{ + const SysprofCaptureSample *sample = SYSPROF_DOCUMENT_FRAME_GET (traceable, SysprofCaptureSample); + + return SYSPROF_DOCUMENT_FRAME_UINT64 (traceable, sample->addrs[position]); +} + +static void +traceable_iface_init (SysprofDocumentTraceableInterface *iface) +{ + iface->get_stack_depth = sysprof_document_sample_get_stack_depth; + iface->get_stack_address = sysprof_document_sample_get_stack_address; +} + +G_DEFINE_FINAL_TYPE_WITH_CODE (SysprofDocumentSample, sysprof_document_sample, SYSPROF_TYPE_DOCUMENT_FRAME, + G_IMPLEMENT_INTERFACE (SYSPROF_TYPE_DOCUMENT_TRACEABLE, traceable_iface_init)) static GParamSpec *properties [N_PROPS]; From 760d573f92dd18352434d3f27e80bc6153bc8a1f Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 28 Apr 2023 13:16:07 -0700 Subject: [PATCH 0033/1030] libsysprof-analyze: move stack-depth to SysprofDocumentTraceable This makes the property for depth a requirement, which will allow for easier quierying from various listmodel filters. --- .../sysprof-document-allocation.c | 27 +++++++++++++++++++ .../sysprof-document-sample.c | 24 +++++------------ .../sysprof-document-sample.h | 6 ++--- .../sysprof-document-traceable.c | 15 +++++++++++ 4 files changed, 50 insertions(+), 22 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-document-allocation.c b/src/libsysprof-analyze/sysprof-document-allocation.c index 33314fb5..0a287339 100644 --- a/src/libsysprof-analyze/sysprof-document-allocation.c +++ b/src/libsysprof-analyze/sysprof-document-allocation.c @@ -40,6 +40,7 @@ enum { PROP_ADDRESS, PROP_IS_FREE, PROP_SIZE, + PROP_STACK_DEPTH, PROP_TID, N_PROPS }; @@ -95,6 +96,10 @@ sysprof_document_allocation_get_property (GObject *object, g_value_set_int64 (value, sysprof_document_allocation_get_size (self)); break; + case PROP_STACK_DEPTH: + g_value_set_uint (value, sysprof_document_traceable_get_stack_depth (SYSPROF_DOCUMENT_TRACEABLE (self))); + break; + case PROP_TID: g_value_set_int (value, sysprof_document_allocation_get_tid (self)); break; @@ -141,17 +146,39 @@ sysprof_document_allocation_class_init (SysprofDocumentAllocationClass *klass) * SysprofDocumentAllocation:size: * * The size of the memory that was allocated or freed. + * + * Since: 45 */ properties [PROP_SIZE] = g_param_spec_int64 ("size", NULL, NULL, G_MININT64, G_MAXINT64, 0, (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + /** + * SysprofDocumentAllocation:is-free: + * + * If this allocation record is a call to release + * #SysprofDocumentAllocation:address. + * + * Since: 45 + */ properties [PROP_IS_FREE] = g_param_spec_boolean ("is-free", NULL, NULL, FALSE, (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + /** + * SysprofDocumentAllocation:stack-depth: + * + * The depth of the stack trace. + * + * Since: 45 + */ + properties [PROP_STACK_DEPTH] = + g_param_spec_uint ("stack-depth", NULL, NULL, + 0, G_MAXUINT16, 0, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_properties (object_class, N_PROPS, properties); } diff --git a/src/libsysprof-analyze/sysprof-document-sample.c b/src/libsysprof-analyze/sysprof-document-sample.c index a7c8fa23..685668d7 100644 --- a/src/libsysprof-analyze/sysprof-document-sample.c +++ b/src/libsysprof-analyze/sysprof-document-sample.c @@ -37,7 +37,7 @@ struct _SysprofDocumentSampleClass enum { PROP_0, - PROP_DEPTH, + PROP_STACK_DEPTH, PROP_TID, N_PROPS }; @@ -81,8 +81,8 @@ sysprof_document_sample_get_property (GObject *object, switch (prop_id) { - case PROP_DEPTH: - g_value_set_uint (value, sysprof_document_sample_get_depth (self)); + case PROP_STACK_DEPTH: + g_value_set_uint (value, sysprof_document_traceable_get_stack_depth (SYSPROF_DOCUMENT_TRACEABLE (self))); break; case PROP_TID: @@ -116,14 +116,14 @@ sysprof_document_sample_class_init (SysprofDocumentSampleClass *klass) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); /** - * SysprofDocumentSample:depth: + * SysprofDocumentSample:stack-depth: * * The depth of the stack trace. * * Since: 45 */ - properties [PROP_DEPTH] = - g_param_spec_uint ("depth", NULL, NULL, + properties [PROP_STACK_DEPTH] = + g_param_spec_uint ("stack-depth", NULL, NULL, 0, G_MAXUINT32, 0, (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); @@ -135,18 +135,6 @@ sysprof_document_sample_init (SysprofDocumentSample *self) { } -guint -sysprof_document_sample_get_depth (SysprofDocumentSample *self) -{ - const SysprofCaptureSample *sample; - - g_return_val_if_fail (SYSPROF_IS_DOCUMENT_SAMPLE (self), 0); - - sample = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureSample); - - return SYSPROF_DOCUMENT_FRAME_UINT32 (self, sample->n_addrs); -} - int sysprof_document_sample_get_tid (SysprofDocumentSample *self) { diff --git a/src/libsysprof-analyze/sysprof-document-sample.h b/src/libsysprof-analyze/sysprof-document-sample.h index 6868fc84..b722540b 100644 --- a/src/libsysprof-analyze/sysprof-document-sample.h +++ b/src/libsysprof-analyze/sysprof-document-sample.h @@ -33,11 +33,9 @@ typedef struct _SysprofDocumentSample SysprofDocumentSample; typedef struct _SysprofDocumentSampleClass SysprofDocumentSampleClass; SYSPROF_AVAILABLE_IN_ALL -GType sysprof_document_sample_get_type (void) G_GNUC_CONST; +GType sysprof_document_sample_get_type (void) G_GNUC_CONST; SYSPROF_AVAILABLE_IN_ALL -guint sysprof_document_sample_get_depth (SysprofDocumentSample *self); -SYSPROF_AVAILABLE_IN_ALL -int sysprof_document_sample_get_tid (SysprofDocumentSample *self); +int sysprof_document_sample_get_tid (SysprofDocumentSample *self); G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofDocumentSample, g_object_unref) diff --git a/src/libsysprof-analyze/sysprof-document-traceable.c b/src/libsysprof-analyze/sysprof-document-traceable.c index 2b6442c7..6525be20 100644 --- a/src/libsysprof-analyze/sysprof-document-traceable.c +++ b/src/libsysprof-analyze/sysprof-document-traceable.c @@ -28,6 +28,21 @@ G_DEFINE_INTERFACE (SysprofDocumentTraceable, sysprof_document_traceable, SYSPRO static void sysprof_document_traceable_default_init (SysprofDocumentTraceableInterface *iface) { + /** + * SysprofDocumentTraceable:stack-depth: + * + * The "stack-depth" property contains the number of addresses collected + * in the backtrace. + * + * You may use this value to retrieve the addresses from 0 to ("stack-depth"-1) + * by calling sysprof_document_traceable_get_stack_address(). + * + * Since: 45 + */ + g_object_interface_install_property (iface, + g_param_spec_uint ("stack-depth", NULL, NULL, + 0, G_MAXUINT16, 0, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS))); } guint From 1c0790a0a2136706d3079010496a8861620d4f08 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 28 Apr 2023 13:16:15 -0700 Subject: [PATCH 0034/1030] libsysprof-analyze: cleanup printf format --- src/libsysprof-analyze/tests/test-capture-model.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libsysprof-analyze/tests/test-capture-model.c b/src/libsysprof-analyze/tests/test-capture-model.c index 04d36e13..b2de4471 100644 --- a/src/libsysprof-analyze/tests/test-capture-model.c +++ b/src/libsysprof-analyze/tests/test-capture-model.c @@ -67,7 +67,7 @@ main (int argc, g_print (" 0x%016"G_GINT64_MODIFIER"x: free", sysprof_document_allocation_get_address (SYSPROF_DOCUMENT_ALLOCATION (frame))); else - g_print (" 0x%016"G_GINT64_MODIFIER"x: allocate %"G_GUINT64_FORMAT" at ", + g_print (" 0x%016"G_GINT64_MODIFIER"x: allocate %"G_GUINT64_FORMAT, sysprof_document_allocation_get_address (SYSPROF_DOCUMENT_ALLOCATION (frame)), sysprof_document_allocation_get_size (SYSPROF_DOCUMENT_ALLOCATION (frame))); } From 1d9de6fb28e7afcaffa995ce1e0a147dd6080177 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 28 Apr 2023 13:19:24 -0700 Subject: [PATCH 0035/1030] libsysprof-analyze: print stack depth on frame info --- src/libsysprof-analyze/tests/test-capture-model.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libsysprof-analyze/tests/test-capture-model.c b/src/libsysprof-analyze/tests/test-capture-model.c index b2de4471..7719525f 100644 --- a/src/libsysprof-analyze/tests/test-capture-model.c +++ b/src/libsysprof-analyze/tests/test-capture-model.c @@ -72,6 +72,10 @@ main (int argc, sysprof_document_allocation_get_size (SYSPROF_DOCUMENT_ALLOCATION (frame))); } + if (SYSPROF_IS_DOCUMENT_TRACEABLE (frame)) + g_print (" stack-depth=%u", + sysprof_document_traceable_get_stack_depth (SYSPROF_DOCUMENT_TRACEABLE (frame))); + g_print ("\n"); g_clear_object (&frame); From 7a3f90e8f515880291c5b95350bf3c3646c5b975 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 28 Apr 2023 16:49:52 -0700 Subject: [PATCH 0036/1030] libsysprof-analyze: add process list model This is not yet functional, but gets the scaffolding in place that we'll use to filter out processes and turn them into something more useful such as their execution lifetime. --- src/libsysprof-analyze/meson.build | 2 + src/libsysprof-analyze/sysprof-analyze.h | 1 + .../sysprof-document-process-list.c | 170 ++++++++++++++++++ .../sysprof-document-process-list.h | 42 +++++ 4 files changed, 215 insertions(+) create mode 100644 src/libsysprof-analyze/sysprof-document-process-list.c create mode 100644 src/libsysprof-analyze/sysprof-document-process-list.h diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build index 76388006..05ad05d3 100644 --- a/src/libsysprof-analyze/meson.build +++ b/src/libsysprof-analyze/meson.build @@ -9,6 +9,7 @@ libsysprof_analyze_public_sources = [ 'sysprof-document-metadata.c', 'sysprof-document-mmap.c', 'sysprof-document-process.c', + 'sysprof-document-process-list.c', 'sysprof-document-sample.c', 'sysprof-document-traceable.c', ] @@ -25,6 +26,7 @@ libsysprof_analyze_public_headers = [ 'sysprof-document-metadata.h', 'sysprof-document-mmap.h', 'sysprof-document-process.h', + 'sysprof-document-process-list.h', 'sysprof-document-sample.h', 'sysprof-document-traceable.h', ] diff --git a/src/libsysprof-analyze/sysprof-analyze.h b/src/libsysprof-analyze/sysprof-analyze.h index ed3f9bd3..133bcb60 100644 --- a/src/libsysprof-analyze/sysprof-analyze.h +++ b/src/libsysprof-analyze/sysprof-analyze.h @@ -35,6 +35,7 @@ G_BEGIN_DECLS # include "sysprof-document-metadata.h" # include "sysprof-document-mmap.h" # include "sysprof-document-process.h" +# include "sysprof-document-process-list.h" # include "sysprof-document-sample.h" # include "sysprof-document-traceable.h" #undef SYSPROF_ANALYZE_INSIDE diff --git a/src/libsysprof-analyze/sysprof-document-process-list.c b/src/libsysprof-analyze/sysprof-document-process-list.c new file mode 100644 index 00000000..e43f8284 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-document-process-list.c @@ -0,0 +1,170 @@ +/* + * sysprof-document-process-list.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-document-process-list.h" + +struct _SysprofDocumentProcessList +{ + GObject parent_instance; + GListModel *model; +}; + +static void list_model_iface_init (GListModelInterface *iface); + +G_DEFINE_FINAL_TYPE_WITH_CODE (SysprofDocumentProcessList, sysprof_document_process_list, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init)) + +enum { + PROP_0, + PROP_MODEL, + N_PROPS +}; + +static GParamSpec *properties [N_PROPS]; + +/** + * sysprof_document_process_list_new: + * @model: a #GListModel or %NULL + * + * Creates a new #SysprofDocumentProcessList. + * + * The resulting process list can be used to determine the lifetime of + * processes that were found within the document. + * + * Returns: (transfer full): a new #SysprofDocumentProcessList + */ +SysprofDocumentProcessList * +sysprof_document_process_list_new (GListModel *model) +{ + g_return_val_if_fail (!model || G_IS_LIST_MODEL (model), NULL); + + return g_object_new (SYSPROF_TYPE_DOCUMENT_PROCESS_LIST, + "model", model, + NULL); +} + +static void +sysprof_document_process_list_finalize (GObject *object) +{ + SysprofDocumentProcessList *self = (SysprofDocumentProcessList *)object; + + g_clear_object (&self->model); + + G_OBJECT_CLASS (sysprof_document_process_list_parent_class)->finalize (object); +} + +static void +sysprof_document_process_list_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofDocumentProcessList *self = SYSPROF_DOCUMENT_PROCESS_LIST (object); + + switch (prop_id) + { + case PROP_MODEL: + g_value_set_object (value, sysprof_document_process_list_get_model (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_document_process_list_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SysprofDocumentProcessList *self = SYSPROF_DOCUMENT_PROCESS_LIST (object); + + switch (prop_id) + { + case PROP_MODEL: + sysprof_document_process_list_set_model (self, g_value_get_object (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_document_process_list_class_init (SysprofDocumentProcessListClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = sysprof_document_process_list_finalize; + object_class->get_property = sysprof_document_process_list_get_property; + object_class->set_property = sysprof_document_process_list_set_property; + + properties [PROP_MODEL] = + g_param_spec_object ("model", NULL, NULL, + G_TYPE_OBJECT, + (G_PARAM_READWRITE | + G_PARAM_EXPLICIT_NOTIFY | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); +} + +static void +sysprof_document_process_list_init (SysprofDocumentProcessList *self) +{ +} + +static void +list_model_iface_init (GListModelInterface *iface) +{ +} + +/** + * sysprof_document_process_list_get_model: + * @self: a #SysprofDocumentProcessList + * + * Gets the underlying model containing processes to extract. + * + * Returns: (transfer none) (nullable): a #GListModel or %NULL + */ +GListModel * +sysprof_document_process_list_get_model (SysprofDocumentProcessList *self) +{ + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_PROCESS_LIST (self), NULL); + + return self->model; +} + +void +sysprof_document_process_list_set_model (SysprofDocumentProcessList *self, + GListModel *model) +{ + g_return_if_fail (SYSPROF_IS_DOCUMENT_PROCESS_LIST (self)); + g_return_if_fail (!model || G_IS_LIST_MODEL (model)); + + if (g_set_object (&self->model, model)) + { + g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_MODEL]); + } +} diff --git a/src/libsysprof-analyze/sysprof-document-process-list.h b/src/libsysprof-analyze/sysprof-document-process-list.h new file mode 100644 index 00000000..72309bae --- /dev/null +++ b/src/libsysprof-analyze/sysprof-document-process-list.h @@ -0,0 +1,42 @@ +/* sysprof-document-process-list.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +#include + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_DOCUMENT_PROCESS_LIST (sysprof_document_process_list_get_type()) + +SYSPROF_AVAILABLE_IN_ALL +G_DECLARE_FINAL_TYPE (SysprofDocumentProcessList, sysprof_document_process_list, SYSPROF, DOCUMENT_PROCESS_LIST, GObject) + +SYSPROF_AVAILABLE_IN_ALL +SysprofDocumentProcessList *sysprof_document_process_list_new (GListModel *model); +SYSPROF_AVAILABLE_IN_ALL +GListModel *sysprof_document_process_list_get_model (SysprofDocumentProcessList *self); +SYSPROF_AVAILABLE_IN_ALL +void sysprof_document_process_list_set_model (SysprofDocumentProcessList *self, + GListModel *model); + +G_END_DECLS From a27d626187395d90e3309b6668c544eb9b19215d Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 28 Apr 2023 16:52:19 -0700 Subject: [PATCH 0037/1030] libsysprof-analyze: install libsysprof-analyze-6.so --- src/libsysprof-analyze/meson.build | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build index 05ad05d3..ba3136b7 100644 --- a/src/libsysprof-analyze/meson.build +++ b/src/libsysprof-analyze/meson.build @@ -36,7 +36,7 @@ libsysprof_analyze_deps = [ libsysprof_capture_deps, ] -libsysprof_analyze = library('sysprof-analyze', +libsysprof_analyze = library('sysprof-analyze-@0@'.format(soname_major_version), libsysprof_analyze_public_sources, include_directories: [include_directories('.'), ipc_include_dirs, @@ -45,6 +45,7 @@ libsysprof_analyze = library('sysprof-analyze', gnu_symbol_visibility: 'hidden', version: '@0@.0.0'.format(soname_major_version), darwin_versions: '@0@.0'.format(soname_major_version), + install: true, ) libsysprof_analyze_dep = declare_dependency( From ffb6533e026904f8323203714ed91b70a0c5588e Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 1 May 2023 11:39:55 -0700 Subject: [PATCH 0038/1030] libsysprof-analyze: add API for intern'ing strings in document We will want to re-use strings as much as we can for resolving symbols, tags, etc. --- .../sysprof-document-private.h | 28 +++++++++++++++++++ src/libsysprof-analyze/sysprof-document.c | 26 +++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 src/libsysprof-analyze/sysprof-document-private.h diff --git a/src/libsysprof-analyze/sysprof-document-private.h b/src/libsysprof-analyze/sysprof-document-private.h new file mode 100644 index 00000000..9be91cd4 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-document-private.h @@ -0,0 +1,28 @@ +/* sysprof-document-private.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +G_BEGIN_DECLS + +const char *sysprof_document_intern_string (SysprofDocument *self, + const char *name); + +G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index 9b9ed75b..5b06838a 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -30,9 +30,14 @@ struct _SysprofDocument { GObject parent_instance; + GArray *frames; GMappedFile *mapped_file; const guint8 *base; + + GMutex strings_mutex; + GStringChunk *strings; + SysprofCaptureFileHeader header; guint needs_swap : 1; }; @@ -91,6 +96,9 @@ sysprof_document_finalize (GObject *object) g_clear_pointer (&self->mapped_file, g_mapped_file_unref); g_clear_pointer (&self->frames, g_array_unref); + g_clear_pointer (&self->strings, g_string_chunk_free); + + g_mutex_clear (&self->strings_mutex); G_OBJECT_CLASS (sysprof_document_parent_class)->finalize (object); } @@ -106,6 +114,8 @@ static void sysprof_document_init (SysprofDocument *self) { self->frames = g_array_new (FALSE, FALSE, sizeof (SysprofDocumentFramePointer)); + self->strings = g_string_chunk_new (4096L * 4); + g_mutex_init (&self->strings_mutex); } static gboolean @@ -236,3 +246,19 @@ sysprof_document_new (const char *filename, return g_steal_pointer (&self); } + +const char * +sysprof_document_intern_string (SysprofDocument *self, + const char *name) +{ + g_return_val_if_fail (SYSPROF_IS_DOCUMENT (self), NULL); + + if (name == NULL) + return NULL; + + g_mutex_lock (&self->strings_mutex); + name = g_string_chunk_insert_const (self->strings, name); + g_mutex_unlock (&self->strings_mutex); + + return name; +} From 4505e8bb85789dc78fb58d69f4c009d581f855c4 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 2 May 2023 13:30:09 -0700 Subject: [PATCH 0039/1030] libsysprof-analyze: use ref strings internally This gives us a bit more control than the string chunk, so long as we can rely on the allocator to do something nice. --- .../sysprof-document-private.h | 6 +++-- src/libsysprof-analyze/sysprof-document.c | 24 ++++++++++++------- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-document-private.h b/src/libsysprof-analyze/sysprof-document-private.h index 9be91cd4..1860fe90 100644 --- a/src/libsysprof-analyze/sysprof-document-private.h +++ b/src/libsysprof-analyze/sysprof-document-private.h @@ -20,9 +20,11 @@ #pragma once +#include "sysprof-document.h" + G_BEGIN_DECLS -const char *sysprof_document_intern_string (SysprofDocument *self, - const char *name); +char *_sysprof_document_ref_string (SysprofDocument *self, + const char *name); G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index 5b06838a..b25521ea 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -36,7 +36,7 @@ struct _SysprofDocument const guint8 *base; GMutex strings_mutex; - GStringChunk *strings; + GHashTable *strings; SysprofCaptureFileHeader header; guint needs_swap : 1; @@ -96,7 +96,7 @@ sysprof_document_finalize (GObject *object) g_clear_pointer (&self->mapped_file, g_mapped_file_unref); g_clear_pointer (&self->frames, g_array_unref); - g_clear_pointer (&self->strings, g_string_chunk_free); + g_clear_pointer (&self->strings, g_hash_table_unref); g_mutex_clear (&self->strings_mutex); @@ -114,7 +114,8 @@ static void sysprof_document_init (SysprofDocument *self) { self->frames = g_array_new (FALSE, FALSE, sizeof (SysprofDocumentFramePointer)); - self->strings = g_string_chunk_new (4096L * 4); + self->strings = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, + (GDestroyNotify)g_ref_string_release); g_mutex_init (&self->strings_mutex); } @@ -247,18 +248,25 @@ sysprof_document_new (const char *filename, return g_steal_pointer (&self); } -const char * -sysprof_document_intern_string (SysprofDocument *self, - const char *name) +char * +_sysprof_document_ref_string (SysprofDocument *self, + const char *name) { + char *ret; + g_return_val_if_fail (SYSPROF_IS_DOCUMENT (self), NULL); if (name == NULL) return NULL; g_mutex_lock (&self->strings_mutex); - name = g_string_chunk_insert_const (self->strings, name); + if (!(ret = g_hash_table_lookup (self->strings, name))) + { + ret = g_ref_string_new (name); + g_hash_table_insert (self->strings, ret, ret); + } + ret = g_ref_string_acquire (ret); g_mutex_unlock (&self->strings_mutex); - return name; + return ret; } From 793740b20ff685b832d900cfa2f61a6cea633416 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 2 May 2023 13:31:22 -0700 Subject: [PATCH 0040/1030] libsysprof-analyze: add SysprofSymbol The goal here is to get an object to represent a symbol so that we can more easily extend things in the future. We'll still use those pointers as unique in the stackstash in the future. --- src/libsysprof-analyze/meson.build | 2 + src/libsysprof-analyze/sysprof-analyze.h | 1 + .../sysprof-symbol-private.h | 32 ++++ src/libsysprof-analyze/sysprof-symbol.c | 155 ++++++++++++++++++ src/libsysprof-analyze/sysprof-symbol.h | 45 +++++ 5 files changed, 235 insertions(+) create mode 100644 src/libsysprof-analyze/sysprof-symbol-private.h create mode 100644 src/libsysprof-analyze/sysprof-symbol.c create mode 100644 src/libsysprof-analyze/sysprof-symbol.h diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build index ba3136b7..430e474f 100644 --- a/src/libsysprof-analyze/meson.build +++ b/src/libsysprof-analyze/meson.build @@ -12,6 +12,7 @@ libsysprof_analyze_public_sources = [ 'sysprof-document-process-list.c', 'sysprof-document-sample.c', 'sysprof-document-traceable.c', + 'sysprof-symbol.c', ] libsysprof_analyze_public_headers = [ @@ -29,6 +30,7 @@ libsysprof_analyze_public_headers = [ 'sysprof-document-process-list.h', 'sysprof-document-sample.h', 'sysprof-document-traceable.h', + 'sysprof-symbol.h', ] libsysprof_analyze_deps = [ diff --git a/src/libsysprof-analyze/sysprof-analyze.h b/src/libsysprof-analyze/sysprof-analyze.h index 133bcb60..735fa683 100644 --- a/src/libsysprof-analyze/sysprof-analyze.h +++ b/src/libsysprof-analyze/sysprof-analyze.h @@ -38,6 +38,7 @@ G_BEGIN_DECLS # include "sysprof-document-process-list.h" # include "sysprof-document-sample.h" # include "sysprof-document-traceable.h" +# include "sysprof-symbol.h" #undef SYSPROF_ANALYZE_INSIDE G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-symbol-private.h b/src/libsysprof-analyze/sysprof-symbol-private.h new file mode 100644 index 00000000..acbc26ec --- /dev/null +++ b/src/libsysprof-analyze/sysprof-symbol-private.h @@ -0,0 +1,32 @@ +/* + * sysprof-symbol-private.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-symbol.h" + +G_BEGIN_DECLS + +SysprofSymbol *_sysprof_symbol_new (char *name, + char *binary_nick, + char *binary_path); + +G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-symbol.c b/src/libsysprof-analyze/sysprof-symbol.c new file mode 100644 index 00000000..66c03c12 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-symbol.c @@ -0,0 +1,155 @@ +/* + * sysprof-symbol.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-symbol.h" + +struct _SysprofSymbol +{ + GObject parent_instance; + + /* All are GRefString */ + char *name; + char *binary_path; + char *binary_nick; +}; + +G_DEFINE_FINAL_TYPE (SysprofSymbol, sysprof_symbol, G_TYPE_OBJECT) + +enum { + PROP_0, + PROP_NAME, + PROP_BINARY_NICK, + PROP_BINARY_PATH, + N_PROPS +}; + +static GParamSpec *properties [N_PROPS]; + +static void +sysprof_symbol_finalize (GObject *object) +{ + SysprofSymbol *self = (SysprofSymbol *)object; + + g_clear_pointer (&self->name, g_ref_string_release); + g_clear_pointer (&self->binary_path, g_ref_string_release); + g_clear_pointer (&self->binary_nick, g_ref_string_release); + + G_OBJECT_CLASS (sysprof_symbol_parent_class)->finalize (object); +} + +static void +sysprof_symbol_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofSymbol *self = SYSPROF_SYMBOL (object); + + switch (prop_id) + { + case PROP_NAME: + g_value_set_string (value, sysprof_symbol_get_name (self)); + break; + + case PROP_BINARY_NICK: + g_value_set_string (value, sysprof_symbol_get_binary_nick (self)); + break; + + case PROP_BINARY_PATH: + g_value_set_string (value, sysprof_symbol_get_binary_path (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_symbol_class_init (SysprofSymbolClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = sysprof_symbol_finalize; + object_class->get_property = sysprof_symbol_get_property; + + properties [PROP_NAME] = + g_param_spec_string ("name", NULL, NULL, + NULL, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + properties [PROP_BINARY_NICK] = + g_param_spec_string ("binary-nick", NULL, NULL, + NULL, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + properties [PROP_BINARY_PATH] = + g_param_spec_string ("binary-path", NULL, NULL, + NULL, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); +} + +static void +sysprof_symbol_init (SysprofSymbol *self) +{ +} + +const char * +sysprof_symbol_get_name (SysprofSymbol *self) +{ + g_return_val_if_fail (SYSPROF_IS_SYMBOL (self), NULL); + + return self->name; +} + +const char * +sysprof_symbol_get_binary_nick (SysprofSymbol *self) +{ + g_return_val_if_fail (SYSPROF_IS_SYMBOL (self), NULL); + + return self->binary_nick; +} + +const char * +sysprof_symbol_get_binary_path (SysprofSymbol *self) +{ + g_return_val_if_fail (SYSPROF_IS_SYMBOL (self), NULL); + + return self->binary_path; +} + +SysprofSymbol * +_sysprof_symbol_new (char *name, + char *binary_path, + char *binary_nick) +{ + SysprofSymbol *self; + + self = g_object_new (SYSPROF_TYPE_SYMBOL, NULL); + self->name = name ? g_ref_string_acquire (name) : NULL; + self->binary_path = binary_path ? g_ref_string_acquire (binary_path) : NULL; + self->binary_nick = binary_nick ? g_ref_string_acquire (binary_nick) : NULL; + + return self; +} diff --git a/src/libsysprof-analyze/sysprof-symbol.h b/src/libsysprof-analyze/sysprof-symbol.h new file mode 100644 index 00000000..01e0522b --- /dev/null +++ b/src/libsysprof-analyze/sysprof-symbol.h @@ -0,0 +1,45 @@ +/* + * sysprof-symbol.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +#include + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_SYMBOL (sysprof_symbol_get_type()) + +SYSPROF_AVAILABLE_IN_ALL +G_DECLARE_FINAL_TYPE (SysprofSymbol, sysprof_symbol, SYSPROF, SYMBOL, GObject) + +SYSPROF_AVAILABLE_IN_ALL +const char *sysprof_symbol_get_name (SysprofSymbol *self); +SYSPROF_AVAILABLE_IN_ALL +const char *sysprof_symbol_get_binary_nick (SysprofSymbol *self); +SYSPROF_AVAILABLE_IN_ALL +const char *sysprof_symbol_get_binary_path (SysprofSymbol *self); +SYSPROF_AVAILABLE_IN_ALL +gboolean sysprof_symbol_equal (const SysprofSymbol *a, + const SysprofSymbol *b); + +G_END_DECLS From 0e799deee4a355c100744bfff4849a983f09e3a7 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 3 May 2023 12:22:08 -0700 Subject: [PATCH 0041/1030] tools: include tid in sample --- src/tools/sysprof-dump.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tools/sysprof-dump.c b/src/tools/sysprof-dump.c index f1d97064..4ca0f2f0 100644 --- a/src/tools/sysprof-dump.c +++ b/src/tools/sysprof-dump.c @@ -288,7 +288,8 @@ main (gint argc, gdouble ptime = (s->frame.time - begin_time) / (gdouble)SYSPROF_NSEC_PER_SEC; SysprofAddressContext context = SYSPROF_ADDRESS_CONTEXT_NONE; - g_print ("SAMPLE: pid=%d time=%" G_GINT64_FORMAT " (%lf)\n", s->frame.pid, s->frame.time, ptime); + g_print ("SAMPLE: pid=%d tid=%d time=%" G_GINT64_FORMAT " (%lf)\n", + s->frame.pid, s->tid, s->frame.time, ptime); for (guint i = 0; i < s->n_addrs; i++) { From f7374f325272da8433f0fedbff2709055704bc79 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 3 May 2023 14:16:31 -0700 Subject: [PATCH 0042/1030] libsysprof-analyze: add minimal symbolizer API This exposes the types and hierarchy but won't expose the API details that we need internally. The idea here is that we can keep that private so we can adjust how it is used should we need to since the public interface would be the SysprofDocumentSymbols, not the SysprofSymbolizer which does decoding directly via address/context state. --- src/libsysprof-analyze/meson.build | 4 + src/libsysprof-analyze/sysprof-analyze.h | 2 + .../sysprof-multi-symbolizer.c | 77 +++++++++++++++++++ .../sysprof-multi-symbolizer.h | 45 +++++++++++ .../sysprof-symbolizer-private.h | 37 +++++++++ src/libsysprof-analyze/sysprof-symbolizer.c | 44 +++++++++++ src/libsysprof-analyze/sysprof-symbolizer.h | 42 ++++++++++ 7 files changed, 251 insertions(+) create mode 100644 src/libsysprof-analyze/sysprof-multi-symbolizer.c create mode 100644 src/libsysprof-analyze/sysprof-multi-symbolizer.h create mode 100644 src/libsysprof-analyze/sysprof-symbolizer-private.h create mode 100644 src/libsysprof-analyze/sysprof-symbolizer.c create mode 100644 src/libsysprof-analyze/sysprof-symbolizer.h diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build index 430e474f..95772299 100644 --- a/src/libsysprof-analyze/meson.build +++ b/src/libsysprof-analyze/meson.build @@ -12,7 +12,9 @@ libsysprof_analyze_public_sources = [ 'sysprof-document-process-list.c', 'sysprof-document-sample.c', 'sysprof-document-traceable.c', + 'sysprof-multi-symbolizer.c', 'sysprof-symbol.c', + 'sysprof-symbolizer.c', ] libsysprof_analyze_public_headers = [ @@ -30,7 +32,9 @@ libsysprof_analyze_public_headers = [ 'sysprof-document-process-list.h', 'sysprof-document-sample.h', 'sysprof-document-traceable.h', + 'sysprof-multi-symbolizer.h', 'sysprof-symbol.h', + 'sysprof-symbolizer.h', ] libsysprof_analyze_deps = [ diff --git a/src/libsysprof-analyze/sysprof-analyze.h b/src/libsysprof-analyze/sysprof-analyze.h index 735fa683..c84348b0 100644 --- a/src/libsysprof-analyze/sysprof-analyze.h +++ b/src/libsysprof-analyze/sysprof-analyze.h @@ -38,7 +38,9 @@ G_BEGIN_DECLS # include "sysprof-document-process-list.h" # include "sysprof-document-sample.h" # include "sysprof-document-traceable.h" +# include "sysprof-multi-symbolizer.h" # include "sysprof-symbol.h" +# include "sysprof-symbolizer.h" #undef SYSPROF_ANALYZE_INSIDE G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-multi-symbolizer.c b/src/libsysprof-analyze/sysprof-multi-symbolizer.c new file mode 100644 index 00000000..2b386907 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-multi-symbolizer.c @@ -0,0 +1,77 @@ +/* sysprof-multi-symbolizer.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-multi-symbolizer.h" +#include "sysprof-symbolizer-private.h" + +struct _SysprofMultiSymbolizer +{ + SysprofSymbolizer parent_instance; + GPtrArray *symbolizers; +}; + +struct _SysprofMultiSymbolizerClass +{ + SysprofSymbolizerClass parent_class; +}; + +G_DEFINE_FINAL_TYPE (SysprofMultiSymbolizer, sysprof_multi_symbolizer, SYSPROF_TYPE_SYMBOLIZER) + +static void +sysprof_multi_symbolizer_finalize (GObject *object) +{ + SysprofMultiSymbolizer *self = (SysprofMultiSymbolizer *)object; + + g_clear_pointer (&self->symbolizers, g_ptr_array_unref); + + G_OBJECT_CLASS (sysprof_multi_symbolizer_parent_class)->finalize (object); +} + +static void +sysprof_multi_symbolizer_class_init (SysprofMultiSymbolizerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = sysprof_multi_symbolizer_finalize; +} + +static void +sysprof_multi_symbolizer_init (SysprofMultiSymbolizer *self) +{ + self->symbolizers = g_ptr_array_new_with_free_func (g_object_unref); +} + +SysprofMultiSymbolizer * +sysprof_multi_symbolizer_new (void) +{ + return g_object_new (SYSPROF_TYPE_MULTI_SYMBOLIZER, NULL); +} + +void +sysprof_multi_symbolizer_add (SysprofMultiSymbolizer *self, + SysprofSymbolizer *symbolizer) +{ + g_return_if_fail (SYSPROF_IS_MULTI_SYMBOLIZER (self)); + g_return_if_fail (SYSPROF_IS_SYMBOLIZER (symbolizer)); + + g_ptr_array_add (self->symbolizers, g_object_ref (symbolizer)); +} diff --git a/src/libsysprof-analyze/sysprof-multi-symbolizer.h b/src/libsysprof-analyze/sysprof-multi-symbolizer.h new file mode 100644 index 00000000..9beffe91 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-multi-symbolizer.h @@ -0,0 +1,45 @@ +/* sysprof-multi-symbolizer.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-symbolizer.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_MULTI_SYMBOLIZER (sysprof_multi_symbolizer_get_type()) +#define SYSPROF_IS_MULTI_SYMBOLIZER(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_MULTI_SYMBOLIZER) +#define SYSPROF_MULTI_SYMBOLIZER(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_MULTI_SYMBOLIZER, SysprofMultiSymbolizer) +#define SYSPROF_MULTI_SYMBOLIZER_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_MULTI_SYMBOLIZER, SysprofMultiSymbolizerClass) + +typedef struct _SysprofMultiSymbolizer SysprofMultiSymbolizer; +typedef struct _SysprofMultiSymbolizerClass SysprofMultiSymbolizerClass; + +SYSPROF_AVAILABLE_IN_ALL +GType sysprof_multi_symbolizer_get_type (void) G_GNUC_CONST; +SYSPROF_AVAILABLE_IN_ALL +SysprofMultiSymbolizer *sysprof_multi_symbolizer_new (void); +SYSPROF_AVAILABLE_IN_ALL +void sysprof_multi_symbolizer_add (SysprofMultiSymbolizer *self, + SysprofSymbolizer *symbolizer); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofMultiSymbolizer, g_object_unref) + +G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-symbolizer-private.h b/src/libsysprof-analyze/sysprof-symbolizer-private.h new file mode 100644 index 00000000..d7f65b75 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-symbolizer-private.h @@ -0,0 +1,37 @@ +/* sysprof-symbolizer-private.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-symbolizer.h" + +G_BEGIN_DECLS + +struct _SysprofSymbolizer +{ + GObject parent; +}; + +struct _SysprofSymbolizerClass +{ + GObjectClass parent_class; +}; + +G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-symbolizer.c b/src/libsysprof-analyze/sysprof-symbolizer.c new file mode 100644 index 00000000..bc8642c1 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-symbolizer.c @@ -0,0 +1,44 @@ +/* sysprof-symbolizer.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-symbolizer-private.h" + +G_DEFINE_ABSTRACT_TYPE (SysprofSymbolizer, sysprof_symbolizer, G_TYPE_OBJECT) + +static void +sysprof_symbolizer_finalize (GObject *object) +{ + G_OBJECT_CLASS (sysprof_symbolizer_parent_class)->finalize (object); +} + +static void +sysprof_symbolizer_class_init (SysprofSymbolizerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = sysprof_symbolizer_finalize; +} + +static void +sysprof_symbolizer_init (SysprofSymbolizer *self) +{ +} diff --git a/src/libsysprof-analyze/sysprof-symbolizer.h b/src/libsysprof-analyze/sysprof-symbolizer.h new file mode 100644 index 00000000..929007bc --- /dev/null +++ b/src/libsysprof-analyze/sysprof-symbolizer.h @@ -0,0 +1,42 @@ +/* sysprof-symbolizer.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +#include + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_SYMBOLIZER (sysprof_symbolizer_get_type()) +#define SYSPROF_IS_SYMBOLIZER(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_SYMBOLIZER) +#define SYSPROF_SYMBOLIZER(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_SYMBOLIZER, SysprofSymbolizer) +#define SYSPROF_SYMBOLIZER_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_SYMBOLIZER, SysprofSymbolizerClass) + +typedef struct _SysprofSymbolizer SysprofSymbolizer; +typedef struct _SysprofSymbolizerClass SysprofSymbolizerClass; + +SYSPROF_AVAILABLE_IN_ALL +GType sysprof_symbolizer_get_type (void) G_GNUC_CONST; + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofSymbolizer, g_object_unref) + +G_END_DECLS From 976fb93a838a4e5df685f15e193a224bfb5f390a Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 3 May 2023 14:21:17 -0700 Subject: [PATCH 0043/1030] libsysprof-analyze: start on symbols API via document --- src/libsysprof-analyze/meson.build | 2 + src/libsysprof-analyze/sysprof-analyze.h | 1 + .../sysprof-document-symbols-private.h | 31 ++++++++ .../sysprof-document-symbols.c | 70 +++++++++++++++++++ .../sysprof-document-symbols.h | 43 ++++++++++++ src/libsysprof-analyze/sysprof-document.h | 23 ++++-- 6 files changed, 165 insertions(+), 5 deletions(-) create mode 100644 src/libsysprof-analyze/sysprof-document-symbols-private.h create mode 100644 src/libsysprof-analyze/sysprof-document-symbols.c create mode 100644 src/libsysprof-analyze/sysprof-document-symbols.h diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build index 95772299..f437ec65 100644 --- a/src/libsysprof-analyze/meson.build +++ b/src/libsysprof-analyze/meson.build @@ -11,6 +11,7 @@ libsysprof_analyze_public_sources = [ 'sysprof-document-process.c', 'sysprof-document-process-list.c', 'sysprof-document-sample.c', + 'sysprof-document-symbols.c', 'sysprof-document-traceable.c', 'sysprof-multi-symbolizer.c', 'sysprof-symbol.c', @@ -31,6 +32,7 @@ libsysprof_analyze_public_headers = [ 'sysprof-document-process.h', 'sysprof-document-process-list.h', 'sysprof-document-sample.h', + 'sysprof-document-symbols.h', 'sysprof-document-traceable.h', 'sysprof-multi-symbolizer.h', 'sysprof-symbol.h', diff --git a/src/libsysprof-analyze/sysprof-analyze.h b/src/libsysprof-analyze/sysprof-analyze.h index c84348b0..9520b530 100644 --- a/src/libsysprof-analyze/sysprof-analyze.h +++ b/src/libsysprof-analyze/sysprof-analyze.h @@ -37,6 +37,7 @@ G_BEGIN_DECLS # include "sysprof-document-process.h" # include "sysprof-document-process-list.h" # include "sysprof-document-sample.h" +# include "sysprof-document-symbols.h" # include "sysprof-document-traceable.h" # include "sysprof-multi-symbolizer.h" # include "sysprof-symbol.h" diff --git a/src/libsysprof-analyze/sysprof-document-symbols-private.h b/src/libsysprof-analyze/sysprof-document-symbols-private.h new file mode 100644 index 00000000..b8d7f44c --- /dev/null +++ b/src/libsysprof-analyze/sysprof-document-symbols-private.h @@ -0,0 +1,31 @@ +/* sysprof-document-symbols-private.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-document.h" +#include "sysprof-document-symbols.h" + +G_BEGIN_DECLS + +SysprofDocumentSymbols *_sysprof_document_symbols_new (SysprofDocument *document, + SysprofSymbolizer *symbolizer); + +G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-document-symbols.c b/src/libsysprof-analyze/sysprof-document-symbols.c new file mode 100644 index 00000000..8ca791b1 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-document-symbols.c @@ -0,0 +1,70 @@ +/* sysprof-document-symbols.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-document-symbols-private.h" + +struct _SysprofDocumentSymbols +{ + GObject parent_instance; + SysprofSymbolizer *symbolizer; +}; + +G_DEFINE_FINAL_TYPE (SysprofDocumentSymbols, sysprof_document_symbols, G_TYPE_OBJECT) + +static void +sysprof_document_symbols_finalize (GObject *object) +{ + SysprofDocumentSymbols *self = (SysprofDocumentSymbols *)object; + + g_clear_object (&self->symbolizer); + + G_OBJECT_CLASS (sysprof_document_symbols_parent_class)->finalize (object); +} + +static void +sysprof_document_symbols_class_init (SysprofDocumentSymbolsClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = sysprof_document_symbols_finalize; +} + +static void +sysprof_document_symbols_init (SysprofDocumentSymbols *self) +{ +} + +SysprofDocumentSymbols * +_sysprof_document_symbols_new (SysprofDocument *document, + SysprofSymbolizer *symbolizer) +{ + SysprofDocumentSymbols *self; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT (document), NULL); + g_return_val_if_fail (SYSPROF_IS_SYMBOLIZER (symbolizer), NULL); + + self = g_object_new (SYSPROF_TYPE_DOCUMENT_SYMBOLS, NULL); + + /* TODO: async generation of symbols */ + + return self; +} diff --git a/src/libsysprof-analyze/sysprof-document-symbols.h b/src/libsysprof-analyze/sysprof-document-symbols.h new file mode 100644 index 00000000..556476f5 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-document-symbols.h @@ -0,0 +1,43 @@ +/* + * sysprof-document-symbols.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +#include + +#include "sysprof-symbol.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_DOCUMENT_SYMBOLS (sysprof_document_symbols_get_type()) + +SYSPROF_AVAILABLE_IN_ALL +G_DECLARE_FINAL_TYPE (SysprofDocumentSymbols, sysprof_document_symbols, SYSPROF, DOCUMENT_SYMBOLS, GObject) + +SYSPROF_AVAILABLE_IN_ALL +SysprofSymbol *sysprof_document_symbols_lookup (SysprofDocumentSymbols *symbols, + int pid, + SysprofAddressContext context, + SysprofAddress address); + +G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-document.h b/src/libsysprof-analyze/sysprof-document.h index 4d76f228..27cdea55 100644 --- a/src/libsysprof-analyze/sysprof-document.h +++ b/src/libsysprof-analyze/sysprof-document.h @@ -22,7 +22,10 @@ #include -#include "sysprof-version-macros.h" +#include + +#include "sysprof-document-symbols.h" +#include "sysprof-symbolizer.h" G_BEGIN_DECLS @@ -32,10 +35,20 @@ SYSPROF_AVAILABLE_IN_ALL G_DECLARE_FINAL_TYPE (SysprofDocument, sysprof_document, SYSPROF, DOCUMENT, GObject) SYSPROF_AVAILABLE_IN_ALL -SysprofDocument *sysprof_document_new (const char *filename, - GError **error); +SysprofDocument *sysprof_document_new (const char *filename, + GError **error); SYSPROF_AVAILABLE_IN_ALL -SysprofDocument *sysprof_document_new_from_fd (int capture_fd, - GError **error); +SysprofDocument *sysprof_document_new_from_fd (int capture_fd, + GError **error); +SYSPROF_AVAILABLE_IN_ALL +void sysprof_document_symbolize_async (SysprofDocument *self, + SysprofSymbolizer *symbolizer, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +SYSPROF_AVAILABLE_IN_ALL +SysprofDocumentSymbols *sysprof_document_symbolize_finish (SysprofDocument *self, + GAsyncResult *result, + GError **error); G_END_DECLS From 426aaad7813e3359243298be287924711702c5c4 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 3 May 2023 14:59:46 -0700 Subject: [PATCH 0044/1030] libsysprof-analyze: add plumbing for symbolization This gets the worker thread going and the state passed to eventually do symbolization through the new symbolizer API. --- .../sysprof-document-symbols-private.h | 9 +- .../sysprof-document-symbols.c | 83 +++++++++++++++---- src/libsysprof-analyze/sysprof-document.c | 56 +++++++++++++ 3 files changed, 128 insertions(+), 20 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-document-symbols-private.h b/src/libsysprof-analyze/sysprof-document-symbols-private.h index b8d7f44c..ad76c901 100644 --- a/src/libsysprof-analyze/sysprof-document-symbols-private.h +++ b/src/libsysprof-analyze/sysprof-document-symbols-private.h @@ -25,7 +25,12 @@ G_BEGIN_DECLS -SysprofDocumentSymbols *_sysprof_document_symbols_new (SysprofDocument *document, - SysprofSymbolizer *symbolizer); +void _sysprof_document_symbols_new (SysprofDocument *document, + SysprofSymbolizer *symbolizer, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +SysprofDocumentSymbols *_sysprof_document_symbols_new_finish (GAsyncResult *result, + GError **error); G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-document-symbols.c b/src/libsysprof-analyze/sysprof-document-symbols.c index 8ca791b1..d0c40352 100644 --- a/src/libsysprof-analyze/sysprof-document-symbols.c +++ b/src/libsysprof-analyze/sysprof-document-symbols.c @@ -24,8 +24,7 @@ struct _SysprofDocumentSymbols { - GObject parent_instance; - SysprofSymbolizer *symbolizer; + GObject parent_instance; }; G_DEFINE_FINAL_TYPE (SysprofDocumentSymbols, sysprof_document_symbols, G_TYPE_OBJECT) @@ -33,10 +32,6 @@ G_DEFINE_FINAL_TYPE (SysprofDocumentSymbols, sysprof_document_symbols, G_TYPE_OB static void sysprof_document_symbols_finalize (GObject *object) { - SysprofDocumentSymbols *self = (SysprofDocumentSymbols *)object; - - g_clear_object (&self->symbolizer); - G_OBJECT_CLASS (sysprof_document_symbols_parent_class)->finalize (object); } @@ -53,18 +48,70 @@ sysprof_document_symbols_init (SysprofDocumentSymbols *self) { } -SysprofDocumentSymbols * -_sysprof_document_symbols_new (SysprofDocument *document, - SysprofSymbolizer *symbolizer) +typedef struct _Symbolize { - SysprofDocumentSymbols *self; + SysprofDocument *document; + SysprofSymbolizer *symbolizer; +} Symbolize; - g_return_val_if_fail (SYSPROF_IS_DOCUMENT (document), NULL); - g_return_val_if_fail (SYSPROF_IS_SYMBOLIZER (symbolizer), NULL); - - self = g_object_new (SYSPROF_TYPE_DOCUMENT_SYMBOLS, NULL); - - /* TODO: async generation of symbols */ - - return self; +static void +symbolize_free (Symbolize *state) +{ + g_clear_object (&state->document); + g_clear_object (&state->symbolizer); + g_free (state); +} + +static void +sysprof_document_symbols_worker (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) +{ + Symbolize *state = task_data; + + g_assert (G_IS_TASK (task)); + g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); + g_assert (state != NULL); + g_assert (SYSPROF_IS_DOCUMENT (state->document)); + g_assert (SYSPROF_IS_SYMBOLIZER (state->symbolizer)); + + g_task_return_new_error (task, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "Not yet supported"); +} + +void +_sysprof_document_symbols_new (SysprofDocument *document, + SysprofSymbolizer *symbolizer, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_autoptr(GTask) task = NULL; + Symbolize *state; + + g_return_if_fail (SYSPROF_IS_DOCUMENT (document)); + g_return_if_fail (SYSPROF_IS_SYMBOLIZER (symbolizer)); + + state = g_new0 (Symbolize, 1); + state->document = g_object_ref (document); + state->symbolizer = g_object_ref (symbolizer); + + task = g_task_new (NULL, cancellable, callback, user_data); + g_task_set_source_tag (task, _sysprof_document_symbols_new); + g_task_set_task_data (task, state, (GDestroyNotify)symbolize_free); + g_task_run_in_thread (task, sysprof_document_symbols_worker); +} + +SysprofDocumentSymbols * +_sysprof_document_symbols_new_finish (GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (G_IS_TASK (result), NULL); + g_return_val_if_fail (g_task_is_valid (result, NULL), NULL); + g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == _sysprof_document_symbols_new, NULL); + + return g_task_propagate_pointer (G_TASK (result), error); } diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index b25521ea..113ff7e2 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -26,6 +26,7 @@ #include "sysprof-document.h" #include "sysprof-document-frame-private.h" +#include "sysprof-document-symbols-private.h" struct _SysprofDocument { @@ -270,3 +271,58 @@ _sysprof_document_ref_string (SysprofDocument *self, return ret; } + +static void +sysprof_document_symbolize_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + g_autoptr(SysprofDocumentSymbols) symbols = NULL; + g_autoptr(GTask) task = user_data; + g_autoptr(GError) error = NULL; + + g_assert (G_IS_ASYNC_RESULT (result)); + g_assert (G_IS_TASK (task)); + + if ((symbols = _sysprof_document_symbols_new_finish (result, &error))) + g_task_return_pointer (task, g_steal_pointer (&symbols), g_object_unref); + else + g_task_return_error (task, g_steal_pointer (&error)); +} + +void +sysprof_document_symbolize_async (SysprofDocument *self, + SysprofSymbolizer *symbolizer, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_autoptr(SysprofDocumentSymbols) symbols = NULL; + g_autoptr(GTask) task = NULL; + + g_return_if_fail (SYSPROF_IS_DOCUMENT (self)); + g_return_if_fail (SYSPROF_IS_SYMBOLIZER (symbolizer)); + g_return_if_fail (!cancellable || SYSPROF_IS_SYMBOLIZER (symbolizer)); + + task = g_task_new (self, cancellable, callback, user_data); + g_task_set_source_tag (task, sysprof_document_symbolize_async); + + _sysprof_document_symbols_new (self, + symbolizer, + cancellable, + sysprof_document_symbolize_cb, + g_steal_pointer (&task)); +} + +SysprofDocumentSymbols * +sysprof_document_symbolize_finish (SysprofDocument *self, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (SYSPROF_IS_DOCUMENT (self), NULL); + g_return_val_if_fail (G_IS_TASK (result), NULL); + g_return_val_if_fail (g_task_is_valid (result, self), NULL); + g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == sysprof_document_symbolize_async, NULL); + + return g_task_propagate_pointer (G_TASK (result), error); +} From f550394b62be352c597934bce52e538ad866aa51 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 3 May 2023 15:31:56 -0700 Subject: [PATCH 0045/1030] libsysprof-analyze: add prepare vfunc for symbolizers This will allow them to pre-process the document and extract whatever information they need out of it to do symbolizing. --- .../sysprof-document-symbols.c | 1 + .../sysprof-multi-symbolizer.c | 86 +++++++++++++++++++ .../sysprof-symbolizer-private.h | 22 +++++ src/libsysprof-analyze/sysprof-symbolizer.c | 47 ++++++++++ 4 files changed, 156 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-document-symbols.c b/src/libsysprof-analyze/sysprof-document-symbols.c index d0c40352..b67ebe9d 100644 --- a/src/libsysprof-analyze/sysprof-document-symbols.c +++ b/src/libsysprof-analyze/sysprof-document-symbols.c @@ -21,6 +21,7 @@ #include "config.h" #include "sysprof-document-symbols-private.h" +#include "sysprof-symbolizer-private.h" struct _SysprofDocumentSymbols { diff --git a/src/libsysprof-analyze/sysprof-multi-symbolizer.c b/src/libsysprof-analyze/sysprof-multi-symbolizer.c index 2b386907..f5dce1b3 100644 --- a/src/libsysprof-analyze/sysprof-multi-symbolizer.c +++ b/src/libsysprof-analyze/sysprof-multi-symbolizer.c @@ -36,6 +36,87 @@ struct _SysprofMultiSymbolizerClass G_DEFINE_FINAL_TYPE (SysprofMultiSymbolizer, sysprof_multi_symbolizer, SYSPROF_TYPE_SYMBOLIZER) +static void +sysprof_multi_symbolizer_prepare_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + SysprofSymbolizer *symbolizer = (SysprofSymbolizer *)object; + g_autoptr(GTask) task = user_data; + g_autoptr(GError) error = NULL; + GPtrArray *state; + + g_assert (SYSPROF_IS_SYMBOLIZER (symbolizer)); + g_assert (G_IS_ASYNC_RESULT (result)); + g_assert (G_IS_TASK (task)); + + state = g_task_get_task_data (task); + + g_assert (state != NULL); + g_assert (state->len > 0); + + if (!_sysprof_symbolizer_prepare_finish (symbolizer, result, &error)) + g_warning ("Failed to initialize symbolizer: %s", error->message); + + g_ptr_array_remove (state, symbolizer); + + if (state->len == 0) + g_task_return_boolean (task, TRUE); +} + +static void +sysprof_multi_symbolizer_prepare_async (SysprofSymbolizer *symbolizer, + SysprofDocument *document, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + SysprofMultiSymbolizer *self = (SysprofMultiSymbolizer *)symbolizer; + g_autoptr(GTask) task = NULL; + g_autoptr(GPtrArray) state = NULL; + + g_assert (SYSPROF_IS_MULTI_SYMBOLIZER (self)); + g_assert (SYSPROF_IS_DOCUMENT (document)); + g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); + + state = g_ptr_array_new_with_free_func (g_object_unref); + for (guint i = 0; i < self->symbolizers->len; i++) + g_ptr_array_add (state, g_object_ref (g_ptr_array_index (self->symbolizers, i))); + + task = g_task_new (self, cancellable, callback, user_data); + g_task_set_source_tag (task, sysprof_multi_symbolizer_prepare_async); + g_task_set_task_data (task, g_ptr_array_ref (state), (GDestroyNotify)g_ptr_array_unref); + + if (state->len == 0) + { + g_task_return_boolean (task, TRUE); + return; + } + + for (guint i = 0; i < state->len; i++) + { + SysprofSymbolizer *child = g_ptr_array_index (state, i); + + _sysprof_symbolizer_prepare_async (child, + document, + cancellable, + sysprof_multi_symbolizer_prepare_cb, + g_object_ref (task)); + } +} + +static gboolean +sysprof_multi_symbolizer_prepare_finish (SysprofSymbolizer *symbolizer, + GAsyncResult *result, + GError **error) +{ + g_assert (SYSPROF_IS_MULTI_SYMBOLIZER (symbolizer)); + g_assert (G_IS_TASK (result)); + g_assert (g_task_is_valid (result, symbolizer)); + + return g_task_propagate_boolean (G_TASK (result), error); +} + static void sysprof_multi_symbolizer_finalize (GObject *object) { @@ -50,8 +131,12 @@ static void sysprof_multi_symbolizer_class_init (SysprofMultiSymbolizerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); + SysprofSymbolizerClass *symbolizer_class = SYSPROF_SYMBOLIZER_CLASS (klass); object_class->finalize = sysprof_multi_symbolizer_finalize; + + symbolizer_class->prepare_async = sysprof_multi_symbolizer_prepare_async; + symbolizer_class->prepare_finish = sysprof_multi_symbolizer_prepare_finish; } static void @@ -72,6 +157,7 @@ sysprof_multi_symbolizer_add (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_ptr_array_add (self->symbolizers, g_object_ref (symbolizer)); } diff --git a/src/libsysprof-analyze/sysprof-symbolizer-private.h b/src/libsysprof-analyze/sysprof-symbolizer-private.h index d7f65b75..c70a110c 100644 --- a/src/libsysprof-analyze/sysprof-symbolizer-private.h +++ b/src/libsysprof-analyze/sysprof-symbolizer-private.h @@ -20,10 +20,13 @@ #pragma once +#include "sysprof-document.h" #include "sysprof-symbolizer.h" G_BEGIN_DECLS +#define SYSPROF_SYMBOLIZER_GET_CLASS(obj) G_TYPE_INSTANCE_GET_CLASS(obj, SYSPROF_TYPE_SYMBOLIZER, SysprofSymbolizerClass) + struct _SysprofSymbolizer { GObject parent; @@ -32,6 +35,25 @@ struct _SysprofSymbolizer struct _SysprofSymbolizerClass { GObjectClass parent_class; + + void (*prepare_async) (SysprofSymbolizer *self, + SysprofDocument *document, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + gboolean (*prepare_finish) (SysprofSymbolizer *self, + GAsyncResult *result, + GError **error); }; + +void _sysprof_symbolizer_prepare_async (SysprofSymbolizer *self, + SysprofDocument *document, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean _sysprof_symbolizer_prepare_finish (SysprofSymbolizer *self, + GAsyncResult *result, + GError **error); + G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-symbolizer.c b/src/libsysprof-analyze/sysprof-symbolizer.c index bc8642c1..8f494903 100644 --- a/src/libsysprof-analyze/sysprof-symbolizer.c +++ b/src/libsysprof-analyze/sysprof-symbolizer.c @@ -24,6 +24,25 @@ G_DEFINE_ABSTRACT_TYPE (SysprofSymbolizer, sysprof_symbolizer, G_TYPE_OBJECT) +static void +sysprof_symbolizer_real_prepare_async (SysprofSymbolizer *self, + SysprofDocument *document, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_autoptr(GTask) task = g_task_new (self, cancellable, callback, user_data); + g_task_return_boolean (task, TRUE); +} + +static gboolean +sysprof_symbolizer_real_prepare_finish (SysprofSymbolizer *self, + GAsyncResult *result, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (result), error); +} + static void sysprof_symbolizer_finalize (GObject *object) { @@ -36,9 +55,37 @@ sysprof_symbolizer_class_init (SysprofSymbolizerClass *klass) GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = sysprof_symbolizer_finalize; + + klass->prepare_async = sysprof_symbolizer_real_prepare_async; + klass->prepare_finish = sysprof_symbolizer_real_prepare_finish; } static void sysprof_symbolizer_init (SysprofSymbolizer *self) { } + +void +_sysprof_symbolizer_prepare_async (SysprofSymbolizer *self, + SysprofDocument *document, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail (SYSPROF_IS_SYMBOLIZER (self)); + g_return_if_fail (SYSPROF_IS_DOCUMENT (document)); + g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); + + SYSPROF_SYMBOLIZER_GET_CLASS (self)->prepare_async (self, document, cancellable, callback, user_data); +} + +gboolean +_sysprof_symbolizer_prepare_finish (SysprofSymbolizer *self, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (SYSPROF_IS_SYMBOLIZER (self), FALSE); + g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE); + + return SYSPROF_SYMBOLIZER_GET_CLASS (self)->prepare_finish (self, result, error); +} From c0ade7da011fbb4a90a6bcf3fe92b78bb881ddeb Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 3 May 2023 17:07:19 -0700 Subject: [PATCH 0046/1030] libsysprof-analyze: add helper for native byte order --- src/libsysprof-analyze/sysprof-document-private.h | 5 +++-- src/libsysprof-analyze/sysprof-document.c | 12 ++++++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-document-private.h b/src/libsysprof-analyze/sysprof-document-private.h index 1860fe90..6af9bd36 100644 --- a/src/libsysprof-analyze/sysprof-document-private.h +++ b/src/libsysprof-analyze/sysprof-document-private.h @@ -24,7 +24,8 @@ G_BEGIN_DECLS -char *_sysprof_document_ref_string (SysprofDocument *self, - const char *name); +gboolean _sysprof_document_is_native (SysprofDocument *self); +char *_sysprof_document_ref_string (SysprofDocument *self, + const char *name); G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index 113ff7e2..d168f9e7 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -122,8 +122,8 @@ sysprof_document_init (SysprofDocument *self) static gboolean sysprof_document_load (SysprofDocument *self, - int capture_fd, - GError **error) + int capture_fd, + GError **error) { goffset pos; gsize len; @@ -326,3 +326,11 @@ sysprof_document_symbolize_finish (SysprofDocument *self, return g_task_propagate_pointer (G_TASK (result), error); } + +gboolean +_sysprof_document_is_native (SysprofDocument *self) +{ + g_return_val_if_fail (SYSPROF_IS_DOCUMENT (self), FALSE); + + return self->needs_swap == FALSE; +} From 95325cc26f6ce1dc1497b47621aac41256ff22f6 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 3 May 2023 17:07:47 -0700 Subject: [PATCH 0047/1030] libsysprof-analyze: add API to load embedded file as gbytes --- src/libsysprof-analyze/sysprof-document.c | 132 ++++++++++++++++++++++ src/libsysprof-analyze/sysprof-document.h | 34 ++++-- 2 files changed, 154 insertions(+), 12 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index d168f9e7..3190d1d1 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -334,3 +334,135 @@ _sysprof_document_is_native (SysprofDocument *self) return self->needs_swap == FALSE; } + +static void +sysprof_document_lookup_file (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) +{ + SysprofDocument *self = source_object; + g_autoptr(GByteArray) bytes = NULL; + const char *filename = task_data; + gboolean is_native; + int filename_len; + + g_assert (G_IS_TASK (task)); + g_assert (SYSPROF_IS_DOCUMENT (source_object)); + g_assert (filename != NULL); + g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); + + bytes = g_byte_array_new (); + is_native = self->needs_swap == FALSE; + filename_len = strlen (filename); + + if (filename_len > 255) + { + g_task_return_new_error (task, + G_IO_ERROR, + G_IO_ERROR_INVALID_FILENAME, + "Filename too long for storage in capture file"); + return; + } + + /* We can access capture data on a thread because the pointers to + * frames are created during construction and then never mutated. + * + * But do remember that frame data may not be byte-swapped. We do + * not need to swap frame->type becaues it's 1 byte. + */ + + for (guint i = 0; i < self->frames->len; i++) + { + const SysprofDocumentFramePointer *ptr = &g_array_index (self->frames, SysprofDocumentFramePointer, i); + const SysprofCaptureFrame *frame = (gpointer)&self->base[ptr->offset]; + const SysprofCaptureFileChunk *chunk; + SysprofCaptureFrameType type = frame->type; + guint16 data_len; + + /* Ignore everything but file chunks */ + if (type != SYSPROF_CAPTURE_FRAME_FILE_CHUNK) + continue; + + chunk = (const SysprofCaptureFileChunk *)(gpointer)frame; + + /* Check path without being certain frame->path is \0 terminatd */ + if (memcmp (filename, chunk->path, filename_len) != 0 || + chunk->path[filename_len] != 0) + continue; + + if (is_native) + data_len = chunk->len; + else +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + data_len = GUINT16_TO_LE (chunk->len); +#else + data_len = GUINT16_TO_BE (chunk->len); +#endif + + /* Check for corrupted file chunk data length */ + if (G_STRUCT_OFFSET (SysprofCaptureFileChunk, data) + data_len > ptr->length) + { + g_byte_array_set_size (bytes, 0); + break; + } + + g_byte_array_append (bytes, chunk->data, data_len); + + if (chunk->is_last) + break; + } + + if (bytes->len == 0) + g_task_return_new_error (task, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "Failed to locate file \"%s\"", filename); + else + g_task_return_pointer (task, + g_byte_array_free_to_bytes (g_steal_pointer (&bytes)), + (GDestroyNotify)g_bytes_unref); +} + +void +sysprof_document_lookup_file_async (SysprofDocument *self, + const char *filename, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_autoptr(GTask) task = NULL; + + g_return_if_fail (SYSPROF_IS_DOCUMENT (self)); + g_return_if_fail (filename != NULL); + g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); + + task = g_task_new (self, cancellable, callback, user_data); + g_task_set_source_tag (task, sysprof_document_lookup_file_async); + g_task_set_task_data (task, g_strdup (filename), g_free); + g_task_run_in_thread (task, sysprof_document_lookup_file); +} + +/** + * sysprof_document_lookup_file_finish: + * @self: a #SysprofDocument + * @result: the #GAsyncResult provided to callback + * @error: a location for a #GError, or %NULL + * + * Completes a request to load the contents of a file that was + * embedded within the document. + * + * Returns: (transfer full): a #GBytes if successful; otherwise %NULL + * and @error is set. + */ +GBytes * +sysprof_document_lookup_file_finish (SysprofDocument *self, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (SYSPROF_IS_DOCUMENT (self), NULL); + g_return_val_if_fail (G_IS_TASK (result), NULL); + g_return_val_if_fail (g_task_is_valid (result, self), NULL); + + return g_task_propagate_pointer (G_TASK (result), error); +} diff --git a/src/libsysprof-analyze/sysprof-document.h b/src/libsysprof-analyze/sysprof-document.h index 27cdea55..fb62bdbb 100644 --- a/src/libsysprof-analyze/sysprof-document.h +++ b/src/libsysprof-analyze/sysprof-document.h @@ -35,20 +35,30 @@ SYSPROF_AVAILABLE_IN_ALL G_DECLARE_FINAL_TYPE (SysprofDocument, sysprof_document, SYSPROF, DOCUMENT, GObject) SYSPROF_AVAILABLE_IN_ALL -SysprofDocument *sysprof_document_new (const char *filename, - GError **error); +SysprofDocument *sysprof_document_new (const char *filename, + GError **error); SYSPROF_AVAILABLE_IN_ALL -SysprofDocument *sysprof_document_new_from_fd (int capture_fd, - GError **error); +SysprofDocument *sysprof_document_new_from_fd (int capture_fd, + GError **error); SYSPROF_AVAILABLE_IN_ALL -void sysprof_document_symbolize_async (SysprofDocument *self, - SysprofSymbolizer *symbolizer, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); +void sysprof_document_symbolize_async (SysprofDocument *self, + SysprofSymbolizer *symbolizer, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); SYSPROF_AVAILABLE_IN_ALL -SysprofDocumentSymbols *sysprof_document_symbolize_finish (SysprofDocument *self, - GAsyncResult *result, - GError **error); +SysprofDocumentSymbols *sysprof_document_symbolize_finish (SysprofDocument *self, + GAsyncResult *result, + GError **error); +SYSPROF_AVAILABLE_IN_ALL +void sysprof_document_lookup_file_async (SysprofDocument *self, + const char *filename, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +SYSPROF_AVAILABLE_IN_ALL +GBytes *sysprof_document_lookup_file_finish (SysprofDocument *self, + GAsyncResult *result, + GError **error); G_END_DECLS From 354eb39676dee2b68bed1651c8c0c65dfcf506c3 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 3 May 2023 17:08:50 -0700 Subject: [PATCH 0048/1030] libsysprof-analyze: add API for bundled symbolizer This, once completed, will use __symbols__ embedded within a capture file to decode symbols rather than querying ELF files directly. --- src/libsysprof-analyze/meson.build | 2 + src/libsysprof-analyze/sysprof-analyze.h | 1 + .../sysprof-bundled-symbolizer.c | 134 ++++++++++++++++++ .../sysprof-bundled-symbolizer.h | 42 ++++++ 4 files changed, 179 insertions(+) create mode 100644 src/libsysprof-analyze/sysprof-bundled-symbolizer.c create mode 100644 src/libsysprof-analyze/sysprof-bundled-symbolizer.h diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build index f437ec65..ab663812 100644 --- a/src/libsysprof-analyze/meson.build +++ b/src/libsysprof-analyze/meson.build @@ -1,4 +1,5 @@ libsysprof_analyze_public_sources = [ + 'sysprof-bundled-symbolizer.c', 'sysprof-document.c', 'sysprof-document-allocation.c', 'sysprof-document-exit.c', @@ -20,6 +21,7 @@ libsysprof_analyze_public_sources = [ libsysprof_analyze_public_headers = [ 'sysprof-analyze.h', + 'sysprof-bundled-symbolizer.h', 'sysprof-document.h', 'sysprof-document-allocation.h', 'sysprof-document-exit.h', diff --git a/src/libsysprof-analyze/sysprof-analyze.h b/src/libsysprof-analyze/sysprof-analyze.h index 9520b530..16db5cee 100644 --- a/src/libsysprof-analyze/sysprof-analyze.h +++ b/src/libsysprof-analyze/sysprof-analyze.h @@ -25,6 +25,7 @@ G_BEGIN_DECLS #define SYSPROF_ANALYZE_INSIDE +# include "sysprof-bundled-symbolizer.h" # include "sysprof-document.h" # include "sysprof-document-allocation.h" # include "sysprof-document-exit.h" diff --git a/src/libsysprof-analyze/sysprof-bundled-symbolizer.c b/src/libsysprof-analyze/sysprof-bundled-symbolizer.c new file mode 100644 index 00000000..f0d4dbc0 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-bundled-symbolizer.c @@ -0,0 +1,134 @@ +/* sysprof-bundled-symbolizer.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-bundled-symbolizer.h" +#include "sysprof-document-private.h" +#include "sysprof-symbolizer-private.h" + +struct _SysprofBundledSymbolizer +{ + SysprofSymbolizer parent_instance; +}; + +struct _SysprofBundledSymbolizerClass +{ + SysprofSymbolizerClass parent_class; +}; + +G_DEFINE_FINAL_TYPE (SysprofBundledSymbolizer, sysprof_bundled_symbolizer, SYSPROF_TYPE_SYMBOLIZER) + +static void +sysprof_bundled_symbolizer_decode (SysprofBundledSymbolizer *self, + GBytes *bytes, + gboolean is_native) +{ + g_assert (SYSPROF_IS_BUNDLED_SYMBOLIZER (self)); + g_assert (bytes != NULL); + +} + +static void +sysprof_bundled_symbolizer_prepare_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + SysprofDocument *document = (SysprofDocument *)object; + g_autoptr(GTask) task = user_data; + g_autoptr(GError) error = NULL; + g_autoptr(GBytes) bytes = NULL; + + g_assert (SYSPROF_IS_DOCUMENT (document)); + g_assert (G_IS_ASYNC_RESULT (result)); + g_assert (G_IS_TASK (task)); + + if ((bytes = sysprof_document_lookup_file_finish (document, result, &error))) + sysprof_bundled_symbolizer_decode (g_task_get_source_object (task), + bytes, + _sysprof_document_is_native (document)); + + g_task_return_boolean (task, TRUE); +} + +static void +sysprof_bundled_symbolizer_prepare_async (SysprofSymbolizer *symbolizer, + SysprofDocument *document, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_autoptr(GTask) task = NULL; + + g_assert (SYSPROF_IS_BUNDLED_SYMBOLIZER (symbolizer)); + g_assert (SYSPROF_IS_DOCUMENT (document)); + g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); + + task = g_task_new (symbolizer, cancellable, callback, user_data); + g_task_set_source_tag (task, sysprof_bundled_symbolizer_prepare_async); + + sysprof_document_lookup_file_async (document, + "__symbols__", + cancellable, + sysprof_bundled_symbolizer_prepare_cb, + g_steal_pointer (&task)); + +} + +static gboolean +sysprof_bundled_symbolizer_prepare_finish (SysprofSymbolizer *symbolizer, + GAsyncResult *result, + GError **error) +{ + g_assert (SYSPROF_IS_BUNDLED_SYMBOLIZER (symbolizer)); + g_assert (G_IS_TASK (result)); + g_assert (g_task_is_valid (result, symbolizer)); + + return g_task_propagate_boolean (G_TASK (result), error); +} + +static void +sysprof_bundled_symbolizer_finalize (GObject *object) +{ + G_OBJECT_CLASS (sysprof_bundled_symbolizer_parent_class)->finalize (object); +} + +static void +sysprof_bundled_symbolizer_class_init (SysprofBundledSymbolizerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + SysprofSymbolizerClass *symbolizer_class = SYSPROF_SYMBOLIZER_CLASS (klass); + + object_class->finalize = sysprof_bundled_symbolizer_finalize; + + symbolizer_class->prepare_async = sysprof_bundled_symbolizer_prepare_async; + symbolizer_class->prepare_finish = sysprof_bundled_symbolizer_prepare_finish; +} + +static void +sysprof_bundled_symbolizer_init (SysprofBundledSymbolizer *self) +{ +} + +SysprofSymbolizer * +sysprof_bundled_symbolizer_new (void) +{ + return g_object_new (SYSPROF_TYPE_BUNDLED_SYMBOLIZER, NULL); +} diff --git a/src/libsysprof-analyze/sysprof-bundled-symbolizer.h b/src/libsysprof-analyze/sysprof-bundled-symbolizer.h new file mode 100644 index 00000000..307d9594 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-bundled-symbolizer.h @@ -0,0 +1,42 @@ +/* sysprof-bundled-symbolizer.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-symbolizer.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_BUNDLED_SYMBOLIZER (sysprof_bundled_symbolizer_get_type()) +#define SYSPROF_IS_BUNDLED_SYMBOLIZER(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_BUNDLED_SYMBOLIZER) +#define SYSPROF_BUNDLED_SYMBOLIZER(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_BUNDLED_SYMBOLIZER, SysprofBundledSymbolizer) +#define SYSPROF_BUNDLED_SYMBOLIZER_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_BUNDLED_SYMBOLIZER, SysprofBundledSymbolizerClass) + +typedef struct _SysprofBundledSymbolizer SysprofBundledSymbolizer; +typedef struct _SysprofBundledSymbolizerClass SysprofBundledSymbolizerClass; + +SYSPROF_AVAILABLE_IN_ALL +GType sysprof_bundled_symbolizer_get_type (void) G_GNUC_CONST; +SYSPROF_AVAILABLE_IN_ALL +SysprofSymbolizer *sysprof_bundled_symbolizer_new (void); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofBundledSymbolizer, g_object_unref) + +G_END_DECLS From 284d0d1f096dcae1e087a2e798428c66394aa1b5 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 3 May 2023 17:09:03 -0700 Subject: [PATCH 0049/1030] libsysprof-analyzer: make multi symbolizer transfer ownership --- src/libsysprof-analyze/sysprof-multi-symbolizer.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/libsysprof-analyze/sysprof-multi-symbolizer.c b/src/libsysprof-analyze/sysprof-multi-symbolizer.c index f5dce1b3..f65846ad 100644 --- a/src/libsysprof-analyze/sysprof-multi-symbolizer.c +++ b/src/libsysprof-analyze/sysprof-multi-symbolizer.c @@ -151,6 +151,14 @@ sysprof_multi_symbolizer_new (void) return g_object_new (SYSPROF_TYPE_MULTI_SYMBOLIZER, NULL); } +/** + * sysprof_multi_symbolizer_add: + * @self: a #SysprofMultiSymbolizer + * @symbolizer: (transfer full): a #SysprofSymbolizer + * + * Takes ownership of @symbolizer and adds it to the list of symbolizers + * that will be queried when @self is queried for symbols. + */ void sysprof_multi_symbolizer_add (SysprofMultiSymbolizer *self, SysprofSymbolizer *symbolizer) @@ -159,5 +167,5 @@ sysprof_multi_symbolizer_add (SysprofMultiSymbolizer *self, g_return_if_fail (SYSPROF_IS_SYMBOLIZER (symbolizer)); g_return_if_fail ((gpointer)self != (gpointer)symbolizer); - g_ptr_array_add (self->symbolizers, g_object_ref (symbolizer)); + g_ptr_array_add (self->symbolizers, symbolizer); } From 5d255bcb8053ce7f31b54dc2a0cda285773f77ea Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 3 May 2023 17:09:18 -0700 Subject: [PATCH 0050/1030] libsysprof-analyze: add simple test for symbolizer API --- src/libsysprof-analyze/tests/meson.build | 3 +- src/libsysprof-analyze/tests/test-symbolize.c | 64 +++++++++++++++++++ 2 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 src/libsysprof-analyze/tests/test-symbolize.c diff --git a/src/libsysprof-analyze/tests/meson.build b/src/libsysprof-analyze/tests/meson.build index d54c296a..e723a994 100644 --- a/src/libsysprof-analyze/tests/meson.build +++ b/src/libsysprof-analyze/tests/meson.build @@ -12,7 +12,8 @@ libsysprof_analyze_testsuite_c_args = [ ] libsysprof_analyze_testsuite = { - 'test-capture-model': {'skip': true}, + 'test-capture-model' : {'skip': true}, + 'test-symbolize' : {'skip': true}, } libsysprof_analyze_testsuite_deps = [ diff --git a/src/libsysprof-analyze/tests/test-symbolize.c b/src/libsysprof-analyze/tests/test-symbolize.c new file mode 100644 index 00000000..62731995 --- /dev/null +++ b/src/libsysprof-analyze/tests/test-symbolize.c @@ -0,0 +1,64 @@ +#include "config.h" + +#include + +static GMainLoop *main_loop; + +static void +symbolize_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + SysprofDocument *document = (SysprofDocument *)object; + g_autoptr(SysprofDocumentSymbols) symbols = NULL; + g_autoptr(GError) error = NULL; + + g_assert (SYSPROF_IS_DOCUMENT (document)); + g_assert (G_IS_ASYNC_RESULT (result)); + + if (!(symbols = sysprof_document_symbolize_finish (document, result, &error))) + g_error ("Failed to symbolize: %s", error->message); + + g_print ("Document symbolized\n"); + + g_main_loop_quit (main_loop); +} + +int +main (int argc, + char *argv[]) +{ + g_autoptr(SysprofDocument) document = NULL; + g_autoptr(SysprofMultiSymbolizer) multi = NULL; + g_autoptr(GError) error = NULL; + const char *filename; + + main_loop = g_main_loop_new (NULL, FALSE); + + if (argc != 2 || !g_file_test (argv[1], G_FILE_TEST_EXISTS)) + { + g_printerr ("usage: %s CAPTURE_FILE\n", argv[0]); + return 1; + } + + filename = argv[1]; + + if (!(document = sysprof_document_new (filename, &error))) + { + g_printerr ("Failed to load document: %s\n", error->message); + return 1; + } + + multi = sysprof_multi_symbolizer_new (); + sysprof_multi_symbolizer_add (multi, sysprof_bundled_symbolizer_new ()); + + sysprof_document_symbolize_async (document, + SYSPROF_SYMBOLIZER (multi), + NULL, + symbolize_cb, + NULL); + + g_main_loop_run (main_loop); + + return 0; +} From b72eda6be24ce51c240fd3c769ae770af61e96ae Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 3 May 2023 17:22:37 -0700 Subject: [PATCH 0051/1030] libsysprof-analyze: prepare symbolizers before decoding --- .../sysprof-document-symbols.c | 9 ++++- src/libsysprof-analyze/sysprof-document.c | 40 +++++++++++++++---- 2 files changed, 39 insertions(+), 10 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-document-symbols.c b/src/libsysprof-analyze/sysprof-document-symbols.c index b67ebe9d..a064ef53 100644 --- a/src/libsysprof-analyze/sysprof-document-symbols.c +++ b/src/libsysprof-analyze/sysprof-document-symbols.c @@ -51,8 +51,9 @@ sysprof_document_symbols_init (SysprofDocumentSymbols *self) typedef struct _Symbolize { - SysprofDocument *document; - SysprofSymbolizer *symbolizer; + SysprofDocument *document; + SysprofSymbolizer *symbolizer; + SysprofDocumentSymbols *symbols; } Symbolize; static void @@ -60,6 +61,7 @@ symbolize_free (Symbolize *state) { g_clear_object (&state->document); g_clear_object (&state->symbolizer); + g_clear_object (&state->symbols); g_free (state); } @@ -71,11 +73,13 @@ sysprof_document_symbols_worker (GTask *task, { Symbolize *state = task_data; + g_assert (source_object == NULL); g_assert (G_IS_TASK (task)); g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); g_assert (state != NULL); g_assert (SYSPROF_IS_DOCUMENT (state->document)); g_assert (SYSPROF_IS_SYMBOLIZER (state->symbolizer)); + g_assert (SYSPROF_IS_DOCUMENT_SYMBOLS (state->symbols)); g_task_return_new_error (task, G_IO_ERROR, @@ -99,6 +103,7 @@ _sysprof_document_symbols_new (SysprofDocument *document, state = g_new0 (Symbolize, 1); state->document = g_object_ref (document); state->symbolizer = g_object_ref (symbolizer); + state->symbols = g_object_new (SYSPROF_TYPE_DOCUMENT_SYMBOLS, NULL); task = g_task_new (NULL, cancellable, callback, user_data); g_task_set_source_tag (task, _sysprof_document_symbols_new); diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index 3190d1d1..9d1285c6 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -27,6 +27,7 @@ #include "sysprof-document.h" #include "sysprof-document-frame-private.h" #include "sysprof-document-symbols-private.h" +#include "sysprof-symbolizer-private.h" struct _SysprofDocument { @@ -273,9 +274,9 @@ _sysprof_document_ref_string (SysprofDocument *self, } static void -sysprof_document_symbolize_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) +sysprof_document_symbolize_symbols_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) { g_autoptr(SysprofDocumentSymbols) symbols = NULL; g_autoptr(GTask) task = user_data; @@ -290,6 +291,29 @@ sysprof_document_symbolize_cb (GObject *object, g_task_return_error (task, g_steal_pointer (&error)); } +static void +sysprof_document_symbolize_prepare_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + SysprofSymbolizer *symbolizer = (SysprofSymbolizer *)object; + g_autoptr(GTask) task = user_data; + g_autoptr(GError) error = NULL; + + g_assert (SYSPROF_IS_SYMBOLIZER (symbolizer)); + g_assert (G_IS_ASYNC_RESULT (result)); + g_assert (G_IS_TASK (task)); + + if (!_sysprof_symbolizer_prepare_finish (symbolizer, result, &error)) + g_task_return_error (task, g_steal_pointer (&error)); + else + _sysprof_document_symbols_new (g_task_get_source_object (task), + symbolizer, + g_task_get_cancellable (task), + sysprof_document_symbolize_symbols_cb, + g_object_ref (task)); +} + void sysprof_document_symbolize_async (SysprofDocument *self, SysprofSymbolizer *symbolizer, @@ -307,11 +331,11 @@ sysprof_document_symbolize_async (SysprofDocument *self, task = g_task_new (self, cancellable, callback, user_data); g_task_set_source_tag (task, sysprof_document_symbolize_async); - _sysprof_document_symbols_new (self, - symbolizer, - cancellable, - sysprof_document_symbolize_cb, - g_steal_pointer (&task)); + _sysprof_symbolizer_prepare_async (symbolizer, + self, + cancellable, + sysprof_document_symbolize_prepare_cb, + g_steal_pointer (&task)); } SysprofDocumentSymbols * From 10c332d5d23397f8f48913c2d08cb7f0f53ca41f Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 3 May 2023 17:44:15 -0700 Subject: [PATCH 0052/1030] libsysprof-analyze: index traceables for faster access This allows us to skip past all the nodes we don't care about with relatively low overhead once the document has been generated. --- src/libsysprof-analyze/meson.build | 2 ++ .../sysprof-document-private.h | 9 ++++--- .../sysprof-document-symbols.c | 21 +++++++++++++++ src/libsysprof-analyze/sysprof-document.c | 26 ++++++++++++++++--- 4 files changed, 52 insertions(+), 6 deletions(-) diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build index ab663812..4fdd21ad 100644 --- a/src/libsysprof-analyze/meson.build +++ b/src/libsysprof-analyze/meson.build @@ -43,6 +43,8 @@ libsysprof_analyze_public_headers = [ libsysprof_analyze_deps = [ dependency('gio-2.0', version: glib_req_version), + dependency('gtk4', version: gtk_req_version), + libsysprof_capture_deps, ] diff --git a/src/libsysprof-analyze/sysprof-document-private.h b/src/libsysprof-analyze/sysprof-document-private.h index 6af9bd36..1e8eee5f 100644 --- a/src/libsysprof-analyze/sysprof-document-private.h +++ b/src/libsysprof-analyze/sysprof-document-private.h @@ -20,12 +20,15 @@ #pragma once +#include + #include "sysprof-document.h" G_BEGIN_DECLS -gboolean _sysprof_document_is_native (SysprofDocument *self); -char *_sysprof_document_ref_string (SysprofDocument *self, - const char *name); +gboolean _sysprof_document_is_native (SysprofDocument *self); +char *_sysprof_document_ref_string (SysprofDocument *self, + const char *name); +GtkBitset *_sysprof_document_samples (SysprofDocument *self); G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-document-symbols.c b/src/libsysprof-analyze/sysprof-document-symbols.c index a064ef53..4116fa81 100644 --- a/src/libsysprof-analyze/sysprof-document-symbols.c +++ b/src/libsysprof-analyze/sysprof-document-symbols.c @@ -20,7 +20,9 @@ #include "config.h" +#include "sysprof-document-private.h" #include "sysprof-document-symbols-private.h" +#include "sysprof-document-traceable.h" #include "sysprof-symbolizer-private.h" struct _SysprofDocumentSymbols @@ -72,6 +74,10 @@ sysprof_document_symbols_worker (GTask *task, GCancellable *cancellable) { Symbolize *state = task_data; + GtkBitsetIter iter; + GtkBitset *bitset; + GListModel *model; + guint i; g_assert (source_object == NULL); g_assert (G_IS_TASK (task)); @@ -81,6 +87,21 @@ sysprof_document_symbols_worker (GTask *task, g_assert (SYSPROF_IS_SYMBOLIZER (state->symbolizer)); g_assert (SYSPROF_IS_DOCUMENT_SYMBOLS (state->symbols)); + bitset = _sysprof_document_samples (state->document); + model = G_LIST_MODEL (state->document); + + if (gtk_bitset_iter_init_first (&iter, bitset, &i)) + { + do + { + g_autoptr(SysprofDocumentTraceable) traceable = g_list_model_get_item (model, i); + + if (!SYSPROF_IS_DOCUMENT_TRACEABLE (traceable)) + continue; + } + while (gtk_bitset_iter_next (&iter, &i)); + } + g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index 9d1285c6..3f767edb 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -24,7 +24,7 @@ #include -#include "sysprof-document.h" +#include "sysprof-document-private.h" #include "sysprof-document-frame-private.h" #include "sysprof-document-symbols-private.h" #include "sysprof-symbolizer-private.h" @@ -37,6 +37,8 @@ struct _SysprofDocument GMappedFile *mapped_file; const guint8 *base; + GtkBitset *samples; + GMutex strings_mutex; GHashTable *strings; @@ -99,6 +101,7 @@ sysprof_document_finalize (GObject *object) g_clear_pointer (&self->mapped_file, g_mapped_file_unref); g_clear_pointer (&self->frames, g_array_unref); g_clear_pointer (&self->strings, g_hash_table_unref); + g_clear_pointer (&self->samples, gtk_bitset_unref); g_mutex_clear (&self->strings_mutex); @@ -115,10 +118,12 @@ sysprof_document_class_init (SysprofDocumentClass *klass) static void sysprof_document_init (SysprofDocument *self) { + g_mutex_init (&self->strings_mutex); + self->frames = g_array_new (FALSE, FALSE, sizeof (SysprofDocumentFramePointer)); self->strings = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify)g_ref_string_release); - g_mutex_init (&self->strings_mutex); + self->samples = gtk_bitset_new_empty (); } static gboolean @@ -158,6 +163,7 @@ sysprof_document_load (SysprofDocument *self, pos = sizeof self->header; while (pos < (len - sizeof(guint16))) { + const SysprofCaptureFrame *tainted; SysprofDocumentFramePointer ptr; guint16 frame_len; @@ -170,9 +176,15 @@ sysprof_document_load (SysprofDocument *self, ptr.offset = pos; ptr.length = frame_len; - g_array_append_val (self->frames, ptr); + + tainted = (const SysprofCaptureFrame *)(gpointer)&self->base[pos]; + if (tainted->type == SYSPROF_CAPTURE_FRAME_SAMPLE || + tainted->type == SYSPROF_CAPTURE_FRAME_ALLOCATION) + gtk_bitset_add (self->samples, self->frames->len); pos += frame_len; + + g_array_append_val (self->frames, ptr); } return TRUE; @@ -490,3 +502,11 @@ sysprof_document_lookup_file_finish (SysprofDocument *self, return g_task_propagate_pointer (G_TASK (result), error); } + +GtkBitset * +_sysprof_document_samples (SysprofDocument *self) +{ + g_return_val_if_fail (SYSPROF_IS_DOCUMENT (self), NULL); + + return self->samples; +} From 00d8081bfe0e4fea36d0d569cd6c3963e1cd3c67 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 3 May 2023 17:50:51 -0700 Subject: [PATCH 0053/1030] libsysprof-analyze: break out helper to for adding traceable --- .../sysprof-document-symbols.c | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-document-symbols.c b/src/libsysprof-analyze/sysprof-document-symbols.c index 4116fa81..131cdebe 100644 --- a/src/libsysprof-analyze/sysprof-document-symbols.c +++ b/src/libsysprof-analyze/sysprof-document-symbols.c @@ -67,6 +67,17 @@ symbolize_free (Symbolize *state) g_free (state); } +static void +sysprof_document_symbols_add_traceable (SysprofDocumentSymbols *self, + SysprofDocumentTraceable *traceable, + SysprofSymbolizer *symbolizer) +{ + g_assert (SYSPROF_IS_DOCUMENT_SYMBOLS (self)); + g_assert (SYSPROF_IS_DOCUMENT_TRACEABLE (traceable)); + g_assert (SYSPROF_IS_SYMBOLIZER (symbolizer)); + +} + static void sysprof_document_symbols_worker (GTask *task, gpointer source_object, @@ -96,16 +107,17 @@ sysprof_document_symbols_worker (GTask *task, { g_autoptr(SysprofDocumentTraceable) traceable = g_list_model_get_item (model, i); - if (!SYSPROF_IS_DOCUMENT_TRACEABLE (traceable)) - continue; + if (SYSPROF_IS_DOCUMENT_TRACEABLE (traceable)) + sysprof_document_symbols_add_traceable (state->symbols, + traceable, + state->symbolizer); } while (gtk_bitset_iter_next (&iter, &i)); } - g_task_return_new_error (task, - G_IO_ERROR, - G_IO_ERROR_NOT_SUPPORTED, - "Not yet supported"); + g_task_return_pointer (task, + g_object_ref (state->symbols), + g_object_unref); } void From e8531ebe58bab16b09d2b312609668016834f908 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 4 May 2023 10:02:53 -0700 Subject: [PATCH 0054/1030] libsysprof-analyze: add batch getter for stack addresses This allows caching the interface vfunc to reduce the overhead of accessing each instruction pointer in the array. --- .../sysprof-document-traceable.c | 21 +++++++++++++++++++ .../sysprof-document-traceable.h | 10 ++++++--- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-document-traceable.c b/src/libsysprof-analyze/sysprof-document-traceable.c index 6525be20..8bf4f250 100644 --- a/src/libsysprof-analyze/sysprof-document-traceable.c +++ b/src/libsysprof-analyze/sysprof-document-traceable.c @@ -57,3 +57,24 @@ sysprof_document_traceable_get_stack_address (SysprofDocumentTraceable *self, { return SYSPROF_DOCUMENT_TRACEABLE_GET_IFACE (self)->get_stack_address (self, position); } + +guint +sysprof_document_traceable_get_stack_addresses (SysprofDocumentTraceable *self, + guint64 *addresses, + guint n_addresses) +{ + guint64 (*decoder) (SysprofDocumentTraceable *, guint); + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_TRACEABLE (self), 0); + + if (addresses == NULL || n_addresses == 0) + return 0; + + decoder = SYSPROF_DOCUMENT_TRACEABLE_GET_IFACE (self)->get_stack_address; + n_addresses = MIN (n_addresses, sysprof_document_traceable_get_stack_depth (self)); + + for (guint i = 0; i < n_addresses; i++) + addresses[i] = decoder (self, i); + + return n_addresses; +} diff --git a/src/libsysprof-analyze/sysprof-document-traceable.h b/src/libsysprof-analyze/sysprof-document-traceable.h index ad77117e..904ac65e 100644 --- a/src/libsysprof-analyze/sysprof-document-traceable.h +++ b/src/libsysprof-analyze/sysprof-document-traceable.h @@ -40,9 +40,13 @@ struct _SysprofDocumentTraceableInterface }; SYSPROF_AVAILABLE_IN_ALL -guint sysprof_document_traceable_get_stack_depth (SysprofDocumentTraceable *self); +guint sysprof_document_traceable_get_stack_depth (SysprofDocumentTraceable *self); SYSPROF_AVAILABLE_IN_ALL -guint64 sysprof_document_traceable_get_stack_address (SysprofDocumentTraceable *self, - guint position); +guint64 sysprof_document_traceable_get_stack_address (SysprofDocumentTraceable *self, + guint position); +SYSPROF_AVAILABLE_IN_ALL +guint sysprof_document_traceable_get_stack_addresses (SysprofDocumentTraceable *self, + guint64 *addresses, + guint n_addresses); G_END_DECLS From c282b54d416273457dcf61f40cd9a9fab588a9ee Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 4 May 2023 10:03:05 -0700 Subject: [PATCH 0055/1030] libsysprof-analyze: fetch the decoded addresses onto the stack --- src/libsysprof-analyze/sysprof-document-symbols.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-document-symbols.c b/src/libsysprof-analyze/sysprof-document-symbols.c index 131cdebe..d6c796ba 100644 --- a/src/libsysprof-analyze/sysprof-document-symbols.c +++ b/src/libsysprof-analyze/sysprof-document-symbols.c @@ -72,10 +72,17 @@ sysprof_document_symbols_add_traceable (SysprofDocumentSymbols *self, SysprofDocumentTraceable *traceable, SysprofSymbolizer *symbolizer) { + guint64 *addresses; + guint n_addresses; + g_assert (SYSPROF_IS_DOCUMENT_SYMBOLS (self)); g_assert (SYSPROF_IS_DOCUMENT_TRACEABLE (traceable)); g_assert (SYSPROF_IS_SYMBOLIZER (symbolizer)); + n_addresses = sysprof_document_traceable_get_stack_depth (traceable); + addresses = g_alloca (sizeof (guint64) * n_addresses); + sysprof_document_traceable_get_stack_addresses (traceable, addresses, n_addresses); + } static void From d2331c888c66754b7263d4d56689a7049f1a6563 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 4 May 2023 10:14:19 -0700 Subject: [PATCH 0056/1030] libsysprof-analyze: fix link dep --- src/libsysprof-analyze/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build index 4fdd21ad..8d510d81 100644 --- a/src/libsysprof-analyze/meson.build +++ b/src/libsysprof-analyze/meson.build @@ -45,7 +45,7 @@ libsysprof_analyze_deps = [ dependency('gio-2.0', version: glib_req_version), dependency('gtk4', version: gtk_req_version), - libsysprof_capture_deps, + libsysprof_capture_dep, ] libsysprof_analyze = library('sysprof-analyze-@0@'.format(soname_major_version), From 377e8e26c6d9d3e1ff0bac233105c6b6ae5a5d76 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 4 May 2023 10:14:43 -0700 Subject: [PATCH 0057/1030] libsysprof-analyze: print some basic info for debugging --- src/libsysprof-analyze/sysprof-document-symbols.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-document-symbols.c b/src/libsysprof-analyze/sysprof-document-symbols.c index d6c796ba..e3ec9eac 100644 --- a/src/libsysprof-analyze/sysprof-document-symbols.c +++ b/src/libsysprof-analyze/sysprof-document-symbols.c @@ -72,6 +72,7 @@ sysprof_document_symbols_add_traceable (SysprofDocumentSymbols *self, SysprofDocumentTraceable *traceable, SysprofSymbolizer *symbolizer) { + SysprofAddressContext last_context; guint64 *addresses; guint n_addresses; @@ -83,6 +84,18 @@ sysprof_document_symbols_add_traceable (SysprofDocumentSymbols *self, addresses = g_alloca (sizeof (guint64) * n_addresses); sysprof_document_traceable_get_stack_addresses (traceable, addresses, n_addresses); + last_context = SYSPROF_ADDRESS_CONTEXT_NONE; + + for (guint i = 0; i < n_addresses; i++) + { + SysprofAddress address = addresses[i]; + SysprofAddressContext context; + + if (sysprof_address_is_context_switch (address, &context)) + g_print ("%d: %s\n", i, sysprof_address_context_to_string (context)); + else + g_print ("%d: %p\n", i, (gpointer)address); + } } static void From 16d03ed630f3a52bbc610a621d7dfba7d8488214 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 4 May 2023 13:58:20 -0700 Subject: [PATCH 0058/1030] libsysprof-analyze: add object to represent a mapped file --- src/libsysprof-analyze/meson.build | 7 +- .../sysprof-mapped-file-private.h | 42 ++++ src/libsysprof-analyze/sysprof-mapped-file.c | 197 ++++++++++++++++++ 3 files changed, 245 insertions(+), 1 deletion(-) create mode 100644 src/libsysprof-analyze/sysprof-mapped-file-private.h create mode 100644 src/libsysprof-analyze/sysprof-mapped-file.c diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build index 8d510d81..db9ccadb 100644 --- a/src/libsysprof-analyze/meson.build +++ b/src/libsysprof-analyze/meson.build @@ -19,6 +19,10 @@ libsysprof_analyze_public_sources = [ 'sysprof-symbolizer.c', ] +libsysprof_analyze_private_sources = [ + 'sysprof-mapped-file.c', +] + libsysprof_analyze_public_headers = [ 'sysprof-analyze.h', 'sysprof-bundled-symbolizer.h', @@ -49,7 +53,8 @@ libsysprof_analyze_deps = [ ] libsysprof_analyze = library('sysprof-analyze-@0@'.format(soname_major_version), - libsysprof_analyze_public_sources, + libsysprof_analyze_public_sources + + libsysprof_analyze_private_sources, include_directories: [include_directories('.'), ipc_include_dirs, libsysprof_capture_include_dirs], diff --git a/src/libsysprof-analyze/sysprof-mapped-file-private.h b/src/libsysprof-analyze/sysprof-mapped-file-private.h new file mode 100644 index 00000000..dc666471 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-mapped-file-private.h @@ -0,0 +1,42 @@ +/* sysprof-mapped-file-private.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_MAPPED_FILE (sysprof_mapped_file_get_type()) + +G_DECLARE_FINAL_TYPE (SysprofMappedFile, sysprof_mapped_file, SYSPROF, MAPPED_FILE, GObject) + +SysprofMappedFile *sysprof_mapped_file_new (char *path, + guint64 address, + guint64 offset, + guint64 length, + gint64 inode); +guint64 sysprof_mapped_file_get_address (SysprofMappedFile *self); +guint64 sysprof_mapped_file_get_offset (SysprofMappedFile *self); +guint64 sysprof_mapped_file_get_length (SysprofMappedFile *self); +gint64 sysprof_mapped_file_get_inode (SysprofMappedFile *self); +const char *sysprof_mapped_file_get_path (SysprofMappedFile *self); + +G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-mapped-file.c b/src/libsysprof-analyze/sysprof-mapped-file.c new file mode 100644 index 00000000..87c9d177 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-mapped-file.c @@ -0,0 +1,197 @@ +/* sysprof-mapped-file.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-mapped-file-private.h" + +struct _SysprofMappedFile +{ + GObject parent_instance; + char *path; + guint64 address; + guint64 offset; + guint64 length; + gint64 inode; +}; + +enum { + PROP_0, + PROP_ADDRESS, + PROP_INODE, + PROP_LENGTH, + PROP_OFFSET, + PROP_PATH, + N_PROPS +}; + +G_DEFINE_FINAL_TYPE (SysprofMappedFile, sysprof_mapped_file, G_TYPE_OBJECT) + +static GParamSpec *properties [N_PROPS]; + +static void +sysprof_mapped_file_finalize (GObject *object) +{ + SysprofMappedFile *self = (SysprofMappedFile *)object; + + g_clear_pointer (&self->path, g_ref_string_release); + + G_OBJECT_CLASS (sysprof_mapped_file_parent_class)->finalize (object); +} + +static void +sysprof_mapped_file_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofMappedFile *self = SYSPROF_MAPPED_FILE (object); + + switch (prop_id) + { + case PROP_ADDRESS: + g_value_set_uint64 (value, self->address); + break; + + case PROP_INODE: + g_value_set_int64 (value, self->inode); + break; + + case PROP_LENGTH: + g_value_set_uint64 (value, self->length); + break; + + case PROP_OFFSET: + g_value_set_uint64 (value, self->offset); + break; + + case PROP_PATH: + g_value_set_string (value, self->path); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_mapped_file_class_init (SysprofMappedFileClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = sysprof_mapped_file_finalize; + object_class->get_property = sysprof_mapped_file_get_property; + + properties [PROP_ADDRESS] = + g_param_spec_uint64 ("address", NULL, NULL, + 0, G_MAXUINT64, 0, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + properties [PROP_INODE] = + g_param_spec_int64 ("inode", NULL, NULL, + G_MININT64, G_MAXINT64, 0, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + properties [PROP_LENGTH] = + g_param_spec_uint64 ("length", NULL, NULL, + 0, G_MAXUINT64, 0, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + properties [PROP_OFFSET] = + g_param_spec_uint64 ("offset", NULL, NULL, + 0, G_MAXUINT64, 0, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + properties [PROP_PATH] = + g_param_spec_string ("path", NULL, NULL, + NULL, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); +} + +static void +sysprof_mapped_file_init (SysprofMappedFile *self) +{ +} + +/** + * sysprof_mapped_file_new: + * @path: a ref-string for use with g_ref_string_acquire(). + * @address: the address where the file is mapped + * @offset: the offset of the file which is mapped at @address + * @length: the length of the memory mapping in bytes + * @inode: an optional inode of the mapped file, used for validation checks + * + * Creates a new #SysprofMappedFile which represents a mapping within the + * address range of a process. + * + * Returns: (transfer full): a #SysprofMappedFile + */ +SysprofMappedFile * +sysprof_mapped_file_new (char *path, + guint64 address, + guint64 offset, + guint64 length, + gint64 inode) +{ + SysprofMappedFile *self; + + g_return_val_if_fail (path != NULL, NULL); + + self = g_object_new (SYSPROF_TYPE_MAPPED_FILE, NULL); + self->path = g_ref_string_acquire (path); + self->address = address; + self->offset = offset; + self->length = length; + self->inode = inode; + + return self; +} + +guint64 +sysprof_mapped_file_get_address (SysprofMappedFile *self) +{ + return self->address; +} + +guint64 +sysprof_mapped_file_get_offset (SysprofMappedFile *self) +{ + return self->offset; +} + +guint64 +sysprof_mapped_file_get_length (SysprofMappedFile *self) +{ + return self->length; +} + +gint64 +sysprof_mapped_file_get_inode (SysprofMappedFile *self) +{ + return self->inode; +} + +const char * +sysprof_mapped_file_get_path (SysprofMappedFile *self) +{ + return self->path; +} From 89fb7329a880521c7558a74b94ddd7a2ffe600dc Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 4 May 2023 14:30:02 -0700 Subject: [PATCH 0059/1030] libsysprof-analyze: add memory map and builder objects The goal here is to have a memory map for each of our processes so that we can translate a memory address into an offset within a file. We will then use an upcoming SysprofMountNamespace as a view into a process file system layout to convert the mapped filename into something we can access from the current process (be it on the host, or via flatpak, etc). --- src/libsysprof-analyze/meson.build | 1 + .../sysprof-memory-map-private.h | 58 ++++++++ src/libsysprof-analyze/sysprof-memory-map.c | 125 ++++++++++++++++++ 3 files changed, 184 insertions(+) create mode 100644 src/libsysprof-analyze/sysprof-memory-map-private.h create mode 100644 src/libsysprof-analyze/sysprof-memory-map.c diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build index db9ccadb..d0ba82a3 100644 --- a/src/libsysprof-analyze/meson.build +++ b/src/libsysprof-analyze/meson.build @@ -21,6 +21,7 @@ libsysprof_analyze_public_sources = [ libsysprof_analyze_private_sources = [ 'sysprof-mapped-file.c', + 'sysprof-memory-map.c', ] libsysprof_analyze_public_headers = [ diff --git a/src/libsysprof-analyze/sysprof-memory-map-private.h b/src/libsysprof-analyze/sysprof-memory-map-private.h new file mode 100644 index 00000000..adf740b8 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-memory-map-private.h @@ -0,0 +1,58 @@ +/* sysprof-memory-map-private.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +#include "sysprof-mapped-file-private.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_MEMORY_MAP (sysprof_memory_map_get_type()) + +G_DECLARE_FINAL_TYPE (SysprofMemoryMap, sysprof_memory_map, SYSPROF, MEMORY_MAP, GObject) + +SysprofMemoryMap *sysprof_memory_map_new (GPtrArray *mapped_files); +SysprofMappedFile *sysprof_memory_map_find_at_address (SysprofMemoryMap *self, + guint64 address); + +typedef GPtrArray* SysprofMemoryMapBuilder; + +static inline void +sysprof_memory_map_builder_init (SysprofMemoryMapBuilder *builder) +{ + *builder = g_ptr_array_new_with_free_func (g_object_unref); +} + +static inline void +sysprof_memory_map_builder_add (SysprofMemoryMapBuilder *builder, + SysprofMappedFile *mapped_file) +{ + g_ptr_array_add (*builder, mapped_file); +} + +static inline SysprofMemoryMap * +sysprof_memory_map_builder_end (SysprofMemoryMapBuilder *builder) +{ + return sysprof_memory_map_new (g_steal_pointer (builder)); +} + +G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-memory-map.c b/src/libsysprof-analyze/sysprof-memory-map.c new file mode 100644 index 00000000..4505f54c --- /dev/null +++ b/src/libsysprof-analyze/sysprof-memory-map.c @@ -0,0 +1,125 @@ +/* sysprof-memory-map.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include + +#include "sysprof-memory-map-private.h" + +struct _SysprofMemoryMap +{ + GObject parent_instance; + GPtrArray *mapped_files; +}; + +G_DEFINE_FINAL_TYPE (SysprofMemoryMap, sysprof_memory_map, G_TYPE_OBJECT) + +static void +sysprof_memory_map_finalize (GObject *object) +{ + SysprofMemoryMap *self = (SysprofMemoryMap *)object; + + g_clear_pointer (&self->mapped_files, g_ptr_array_unref); + + G_OBJECT_CLASS (sysprof_memory_map_parent_class)->finalize (object); +} + +static void +sysprof_memory_map_class_init (SysprofMemoryMapClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = sysprof_memory_map_finalize; +} + +static void +sysprof_memory_map_init (SysprofMemoryMap *self) +{ + self->mapped_files = g_ptr_array_new_with_free_func (g_object_unref); +} + +static int +mapped_file_compare (gconstpointer aptr, + gconstpointer bptr) +{ + SysprofMappedFile * const *a = aptr; + SysprofMappedFile * const *b = bptr; + guint64 aaddr = sysprof_mapped_file_get_address (*a); + guint64 baddr = sysprof_mapped_file_get_address (*b); + + if (aaddr < baddr) + return -1; + else if (aaddr > baddr) + return 1; + else + return 0; +} + +static int +mapped_file_bsearch (gconstpointer keyptr, + gconstpointer elemptr) +{ + guint64 address = *(const guint64 *)keyptr; + SysprofMappedFile *mapped_file = *(SysprofMappedFile * const *)elemptr; + guint64 begin = sysprof_mapped_file_get_address (mapped_file); + guint64 length = sysprof_mapped_file_get_length (mapped_file); + + if (address < begin) + return -1; + + if (address >= begin + length) + return 1; + + return 0; +} + +SysprofMemoryMap * +sysprof_memory_map_new (GPtrArray *mapped_files) +{ + SysprofMemoryMap *self; + + g_return_val_if_fail (mapped_files != NULL, NULL); + + g_ptr_array_set_free_func (mapped_files, g_object_unref); + g_ptr_array_sort (mapped_files, mapped_file_compare); + + self = g_object_new (SYSPROF_TYPE_MEMORY_MAP, NULL); + self->mapped_files = mapped_files; + + return self; +} + +SysprofMappedFile * +sysprof_memory_map_find_at_address (SysprofMemoryMap *self, + guint64 address) +{ + SysprofMappedFile **match; + + g_return_val_if_fail (SYSPROF_IS_MEMORY_MAP (self), NULL); + + match = bsearch (&address, + (gpointer)self->mapped_files->pdata, + self->mapped_files->len, + sizeof (gpointer), + mapped_file_bsearch); + + return match ? *match : NULL; +} From 17eaa7a610e5becc0cd0afb02242214449302d76 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 4 May 2023 14:32:33 -0700 Subject: [PATCH 0060/1030] libsysprof-analyze: add various docs These functions have some transfer semantics, so even though they are private API, make sure that we document it for the future. --- src/libsysprof-analyze/sysprof-memory-map.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-memory-map.c b/src/libsysprof-analyze/sysprof-memory-map.c index 4505f54c..6c265416 100644 --- a/src/libsysprof-analyze/sysprof-memory-map.c +++ b/src/libsysprof-analyze/sysprof-memory-map.c @@ -91,6 +91,17 @@ mapped_file_bsearch (gconstpointer keyptr, return 0; } +/** + * sysprof_memory_map_new: + * @mapped_files: (transfer full): a #GPtrArray of #SysprofMappedFile + * + * Creates a new #SysprofMemoryMap that can be used to locate a mapped + * file which contains a particular address within a target process. + * + * You should use SysprofMemoryMapBuilder instead of this function. + * + * Returns: (transfer full): a #SysprofMemoryMap + */ SysprofMemoryMap * sysprof_memory_map_new (GPtrArray *mapped_files) { @@ -107,6 +118,16 @@ sysprof_memory_map_new (GPtrArray *mapped_files) return self; } +/** + * sysprof_memory_map_find_at_address: + * @self: a #SysprofMemoryMap + * @address: an address + * + * Attempts to locate the #SysprofMappedFile which contains @address. + * + * Returns: (transfer none) (nullable): a #SysprofMappedFile if there + * was a mapping containing @address, otherwise %NULL. + */ SysprofMappedFile * sysprof_memory_map_find_at_address (SysprofMemoryMap *self, guint64 address) From ee957ed1d4fa13676efd14bda9f9e89e4557d550 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 5 May 2023 12:44:31 -0700 Subject: [PATCH 0061/1030] libsysprof-analyze: start on mount namespace APIs The goal here is that we can try to reproduce to some degree the mount namespace as the PID saw it, but also convert that to paths where we can find the files on the host system. --- src/libsysprof-analyze/meson.build | 2 + .../sysprof-mount-device-private.h | 42 ++++ src/libsysprof-analyze/sysprof-mount-device.c | 195 ++++++++++++++++++ .../sysprof-mount-namespace-private.h | 46 +++++ .../sysprof-mount-namespace.c | 94 +++++++++ 5 files changed, 379 insertions(+) create mode 100644 src/libsysprof-analyze/sysprof-mount-device-private.h create mode 100644 src/libsysprof-analyze/sysprof-mount-device.c create mode 100644 src/libsysprof-analyze/sysprof-mount-namespace-private.h create mode 100644 src/libsysprof-analyze/sysprof-mount-namespace.c diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build index d0ba82a3..bbe14fe5 100644 --- a/src/libsysprof-analyze/meson.build +++ b/src/libsysprof-analyze/meson.build @@ -22,6 +22,8 @@ libsysprof_analyze_public_sources = [ libsysprof_analyze_private_sources = [ 'sysprof-mapped-file.c', 'sysprof-memory-map.c', + 'sysprof-mount-device.c', + 'sysprof-mount-namespace.c', ] libsysprof_analyze_public_headers = [ diff --git a/src/libsysprof-analyze/sysprof-mount-device-private.h b/src/libsysprof-analyze/sysprof-mount-device-private.h new file mode 100644 index 00000000..cfebc3bd --- /dev/null +++ b/src/libsysprof-analyze/sysprof-mount-device-private.h @@ -0,0 +1,42 @@ +/* sysprof-mount-device-private.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_MOUNT_DEVICE (sysprof_mount_device_get_type()) + +G_DECLARE_FINAL_TYPE (SysprofMountDevice, sysprof_mount_device, SYSPROF, MOUNT_DEVICE, GObject) + +SysprofMountDevice *sysprof_mount_device_new (void); +const char *sysprof_mount_device_get_id (SysprofMountDevice *self); +void sysprof_mount_device_set_id (SysprofMountDevice *self, + const char *id); +const char *sysprof_mount_device_get_mount_path (SysprofMountDevice *self); +void sysprof_mount_device_set_mount_path (SysprofMountDevice *self, + const char *mount_path); +const char *sysprof_mount_device_get_subvolume (SysprofMountDevice *self); +void sysprof_mount_device_set_subvolume (SysprofMountDevice *self, + const char *subvolume); + +G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-mount-device.c b/src/libsysprof-analyze/sysprof-mount-device.c new file mode 100644 index 00000000..e93002b1 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-mount-device.c @@ -0,0 +1,195 @@ +/* sysprof-mount-device.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-mount-device-private.h" + +struct _SysprofMountDevice +{ + GObject parent_instance; + char *id; + char *mount_path; + char *subvolume; +}; + +enum { + PROP_0, + PROP_ID, + PROP_MOUNT_PATH, + PROP_SUBVOLUME, + N_PROPS +}; + +G_DEFINE_FINAL_TYPE (SysprofMountDevice, sysprof_mount_device, G_TYPE_OBJECT) + +static GParamSpec *properties [N_PROPS]; + +static void +sysprof_mount_device_finalize (GObject *object) +{ + SysprofMountDevice *self = (SysprofMountDevice *)object; + + g_clear_pointer (&self->id, g_free); + g_clear_pointer (&self->mount_path, g_free); + g_clear_pointer (&self->subvolume, g_free); + + G_OBJECT_CLASS (sysprof_mount_device_parent_class)->finalize (object); +} + +static void +sysprof_mount_device_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofMountDevice *self = SYSPROF_MOUNT_DEVICE (object); + + switch (prop_id) + { + case PROP_ID: + g_value_set_string (value, sysprof_mount_device_get_id (self)); + break; + + case PROP_MOUNT_PATH: + g_value_set_string (value, sysprof_mount_device_get_mount_path (self)); + break; + + case PROP_SUBVOLUME: + g_value_set_string (value, sysprof_mount_device_get_subvolume (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_mount_device_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SysprofMountDevice *self = SYSPROF_MOUNT_DEVICE (object); + + switch (prop_id) + { + case PROP_ID: + sysprof_mount_device_set_id (self, g_value_get_string (value)); + break; + + case PROP_MOUNT_PATH: + sysprof_mount_device_set_mount_path (self, g_value_get_string (value)); + break; + + case PROP_SUBVOLUME: + sysprof_mount_device_set_subvolume (self, g_value_get_string (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_mount_device_class_init (SysprofMountDeviceClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = sysprof_mount_device_finalize; + object_class->get_property = sysprof_mount_device_get_property; + object_class->set_property = sysprof_mount_device_set_property; + + properties [PROP_ID] = + g_param_spec_string ("id", NULL, NULL, + NULL, + (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); + + properties [PROP_MOUNT_PATH] = + g_param_spec_string ("mount-path", NULL, NULL, + NULL, + (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); + + properties [PROP_SUBVOLUME] = + g_param_spec_string ("subvolume", NULL, NULL, + NULL, + (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); +} + +static void +sysprof_mount_device_init (SysprofMountDevice *self) +{ +} + +const char * +sysprof_mount_device_get_id (SysprofMountDevice *self) +{ + g_return_val_if_fail (SYSPROF_IS_MOUNT_DEVICE (self), NULL); + + return self->id; +} + +void +sysprof_mount_device_set_id (SysprofMountDevice *self, + const char *id) +{ + g_return_if_fail (SYSPROF_IS_MOUNT_DEVICE (self)); + + if (g_set_str (&self->id, id)) + g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ID]); +} + +const char * +sysprof_mount_device_get_mount_path (SysprofMountDevice *self) +{ + g_return_val_if_fail (SYSPROF_IS_MOUNT_DEVICE (self), NULL); + + return self->mount_path; +} + +void +sysprof_mount_device_set_mount_path (SysprofMountDevice *self, + const char *mount_path) +{ + g_return_if_fail (SYSPROF_IS_MOUNT_DEVICE (self)); + + if (g_set_str (&self->mount_path, mount_path)) + g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_MOUNT_PATH]); +} + +const char * +sysprof_mount_device_get_subvolume (SysprofMountDevice *self) +{ + g_return_val_if_fail (SYSPROF_IS_MOUNT_DEVICE (self), NULL); + + return self->subvolume; +} + +void +sysprof_mount_device_set_subvolume (SysprofMountDevice *self, + const char *subvolume) +{ + g_return_if_fail (SYSPROF_IS_MOUNT_DEVICE (self)); + + if (g_set_str (&self->subvolume, subvolume)) + g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_SUBVOLUME]); +} diff --git a/src/libsysprof-analyze/sysprof-mount-namespace-private.h b/src/libsysprof-analyze/sysprof-mount-namespace-private.h new file mode 100644 index 00000000..1a5cea0b --- /dev/null +++ b/src/libsysprof-analyze/sysprof-mount-namespace-private.h @@ -0,0 +1,46 @@ +/* sysprof-mount-namespace-private.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +#include "sysprof-mount-device-private.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_MOUNT_NAMESPACE (sysprof_mount_namespace_get_type()) + +G_DECLARE_FINAL_TYPE (SysprofMountNamespace, sysprof_mount_namespace, SYSPROF, MOUNT_NAMESPACE, GObject) + +SysprofMountNamespace *sysprof_mount_namespace_new (void); +SysprofMountNamespace *sysprof_mount_namespace_copy (SysprofMountNamespace *self); +void sysprof_mount_namespace_add_device (SysprofMountNamespace *self, + SysprofMountDevice *mount); +void sysprof_mount_namespace_add_mount (SysprofMountNamespace *self, + const char *path, + const char *host_path, + int layer); +char **sysprof_mount_namespace_translate (SysprofMountNamespace *self, + const char *path); +GMappedFile *sysprof_mount_namespace_open (SysprofMountNamespace *self, + const char *path); + +G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-mount-namespace.c b/src/libsysprof-analyze/sysprof-mount-namespace.c new file mode 100644 index 00000000..c660b5ed --- /dev/null +++ b/src/libsysprof-analyze/sysprof-mount-namespace.c @@ -0,0 +1,94 @@ +/* sysprof-mount-namespace.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-mount-namespace-private.h" + +struct _SysprofMountNamespace +{ + GObject parent_instance; + GPtrArray *devices; +}; + +G_DEFINE_FINAL_TYPE (SysprofMountNamespace, sysprof_mount_namespace, G_TYPE_OBJECT) + +static void +sysprof_mount_namespace_finalize (GObject *object) +{ + SysprofMountNamespace *self = (SysprofMountNamespace *)object; + + g_clear_pointer (&self->devices, g_ptr_array_unref); + + G_OBJECT_CLASS (sysprof_mount_namespace_parent_class)->finalize (object); +} + +static void +sysprof_mount_namespace_class_init (SysprofMountNamespaceClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = sysprof_mount_namespace_finalize; +} + +static void +sysprof_mount_namespace_init (SysprofMountNamespace *self) +{ + self->devices = g_ptr_array_new_with_free_func (g_object_unref); +} + +SysprofMountNamespace * +sysprof_mount_namespace_new (void) +{ + return g_object_new (SYSPROF_TYPE_MOUNT_NAMESPACE, NULL); +} + +SysprofMountNamespace * +sysprof_mount_namespace_copy (SysprofMountNamespace *self) +{ + SysprofMountNamespace *copy; + + g_return_val_if_fail (SYSPROF_IS_MOUNT_NAMESPACE (self), NULL); + + copy = sysprof_mount_namespace_new (); + + for (guint i = 0; i < self->devices->len; i++) + sysprof_mount_namespace_add_device (copy, g_object_ref (g_ptr_array_index (self->devices, i))); + + return copy; +} + +/** + * sysprof_mount_namespace_add_device: + * @self: a #SysprofMountNamespace + * @device: (transfer full): a #SysprofMountDevice + * + * Adds information about where a device is mounted on the host for resolving + * paths to binaries. + */ +void +sysprof_mount_namespace_add_device (SysprofMountNamespace *self, + SysprofMountDevice *device) +{ + g_return_if_fail (SYSPROF_IS_MOUNT_NAMESPACE (self)); + g_return_if_fail (SYSPROF_IS_MOUNT_DEVICE (device)); + + g_ptr_array_add (self->devices, device); +} From 1c75c4c5487126ad094797f1d77ac2e55746e27d Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 5 May 2023 13:03:03 -0700 Subject: [PATCH 0062/1030] libsysprof-analyze: rename samples to traceables This includes all current traceables, not just samples, so make the name a bit more clear which it is. --- src/libsysprof-analyze/sysprof-document-private.h | 2 +- src/libsysprof-analyze/sysprof-document-symbols.c | 2 +- src/libsysprof-analyze/sysprof-document.c | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-document-private.h b/src/libsysprof-analyze/sysprof-document-private.h index 1e8eee5f..01ab04c8 100644 --- a/src/libsysprof-analyze/sysprof-document-private.h +++ b/src/libsysprof-analyze/sysprof-document-private.h @@ -29,6 +29,6 @@ G_BEGIN_DECLS gboolean _sysprof_document_is_native (SysprofDocument *self); char *_sysprof_document_ref_string (SysprofDocument *self, const char *name); -GtkBitset *_sysprof_document_samples (SysprofDocument *self); +GtkBitset *_sysprof_document_traceables (SysprofDocument *self); G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-document-symbols.c b/src/libsysprof-analyze/sysprof-document-symbols.c index e3ec9eac..c008990d 100644 --- a/src/libsysprof-analyze/sysprof-document-symbols.c +++ b/src/libsysprof-analyze/sysprof-document-symbols.c @@ -118,7 +118,7 @@ sysprof_document_symbols_worker (GTask *task, g_assert (SYSPROF_IS_SYMBOLIZER (state->symbolizer)); g_assert (SYSPROF_IS_DOCUMENT_SYMBOLS (state->symbols)); - bitset = _sysprof_document_samples (state->document); + bitset = _sysprof_document_traceables (state->document); model = G_LIST_MODEL (state->document); if (gtk_bitset_iter_init_first (&iter, bitset, &i)) diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index 3f767edb..94eee0af 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -37,7 +37,7 @@ struct _SysprofDocument GMappedFile *mapped_file; const guint8 *base; - GtkBitset *samples; + GtkBitset *traceables; GMutex strings_mutex; GHashTable *strings; @@ -101,7 +101,7 @@ sysprof_document_finalize (GObject *object) g_clear_pointer (&self->mapped_file, g_mapped_file_unref); g_clear_pointer (&self->frames, g_array_unref); g_clear_pointer (&self->strings, g_hash_table_unref); - g_clear_pointer (&self->samples, gtk_bitset_unref); + g_clear_pointer (&self->traceables, gtk_bitset_unref); g_mutex_clear (&self->strings_mutex); @@ -123,7 +123,7 @@ sysprof_document_init (SysprofDocument *self) self->frames = g_array_new (FALSE, FALSE, sizeof (SysprofDocumentFramePointer)); self->strings = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify)g_ref_string_release); - self->samples = gtk_bitset_new_empty (); + self->traceables = gtk_bitset_new_empty (); } static gboolean @@ -180,7 +180,7 @@ sysprof_document_load (SysprofDocument *self, tainted = (const SysprofCaptureFrame *)(gpointer)&self->base[pos]; if (tainted->type == SYSPROF_CAPTURE_FRAME_SAMPLE || tainted->type == SYSPROF_CAPTURE_FRAME_ALLOCATION) - gtk_bitset_add (self->samples, self->frames->len); + gtk_bitset_add (self->traceables, self->frames->len); pos += frame_len; @@ -504,9 +504,9 @@ sysprof_document_lookup_file_finish (SysprofDocument *self, } GtkBitset * -_sysprof_document_samples (SysprofDocument *self) +_sysprof_document_traceables (SysprofDocument *self) { g_return_val_if_fail (SYSPROF_IS_DOCUMENT (self), NULL); - return self->samples; + return self->traceables; } From 235dd98621ba74a279e355730b76c9c9a91d82be Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 5 May 2023 13:29:06 -0700 Subject: [PATCH 0063/1030] libsysprof-analyze: add SysprofDocumentFileChunk This object will represent frames in the list model which are file chunks. --- src/libsysprof-analyze/meson.build | 2 + src/libsysprof-analyze/sysprof-analyze.h | 1 + .../sysprof-document-file-chunk.c | 156 ++++++++++++++++++ .../sysprof-document-file-chunk.h | 49 ++++++ .../sysprof-document-frame.c | 5 + 5 files changed, 213 insertions(+) create mode 100644 src/libsysprof-analyze/sysprof-document-file-chunk.c create mode 100644 src/libsysprof-analyze/sysprof-document-file-chunk.h diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build index bbe14fe5..91b65de2 100644 --- a/src/libsysprof-analyze/meson.build +++ b/src/libsysprof-analyze/meson.build @@ -3,6 +3,7 @@ libsysprof_analyze_public_sources = [ 'sysprof-document.c', 'sysprof-document-allocation.c', 'sysprof-document-exit.c', + 'sysprof-document-file-chunk.c', 'sysprof-document-fork.c', 'sysprof-document-frame.c', 'sysprof-document-log.c', @@ -32,6 +33,7 @@ libsysprof_analyze_public_headers = [ 'sysprof-document.h', 'sysprof-document-allocation.h', 'sysprof-document-exit.h', + 'sysprof-document-file-chunk.h', 'sysprof-document-fork.h', 'sysprof-document-frame.h', 'sysprof-document-log.h', diff --git a/src/libsysprof-analyze/sysprof-analyze.h b/src/libsysprof-analyze/sysprof-analyze.h index 16db5cee..ba5180dc 100644 --- a/src/libsysprof-analyze/sysprof-analyze.h +++ b/src/libsysprof-analyze/sysprof-analyze.h @@ -29,6 +29,7 @@ G_BEGIN_DECLS # include "sysprof-document.h" # include "sysprof-document-allocation.h" # include "sysprof-document-exit.h" +# include "sysprof-document-file-chunk.h" # include "sysprof-document-fork.h" # include "sysprof-document-frame.h" # include "sysprof-document-log.h" diff --git a/src/libsysprof-analyze/sysprof-document-file-chunk.c b/src/libsysprof-analyze/sysprof-document-file-chunk.c new file mode 100644 index 00000000..97e621f9 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-document-file-chunk.c @@ -0,0 +1,156 @@ +/* sysprof-document-file-chunk.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-document-frame-private.h" + +#include "sysprof-document-file-chunk.h" + +struct _SysprofDocumentFileChunk +{ + SysprofDocumentFrame parent_instance; +}; + +struct _SysprofDocumentFileChunkClass +{ + SysprofDocumentFrameClass parent_instance; +}; + +enum { + PROP_0, + PROP_IS_LAST, + PROP_SIZE, + PROP_PATH, + N_PROPS +}; + +G_DEFINE_FINAL_TYPE (SysprofDocumentFileChunk, sysprof_document_file_chunk, SYSPROF_TYPE_DOCUMENT_FRAME) + +static GParamSpec *properties [N_PROPS]; + +static void +sysprof_document_file_chunk_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofDocumentFileChunk *self = SYSPROF_DOCUMENT_FILE_CHUNK (object); + + switch (prop_id) + { + case PROP_IS_LAST: + g_value_set_boolean (value, sysprof_document_file_chunk_get_is_last (self)); + break; + + case PROP_SIZE: + g_value_set_uint (value, sysprof_document_file_chunk_get_size (self)); + break; + + case PROP_PATH: + g_value_set_string (value, sysprof_document_file_chunk_get_path (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_document_file_chunk_class_init (SysprofDocumentFileChunkClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->get_property = sysprof_document_file_chunk_get_property; + + properties [PROP_IS_LAST] = + g_param_spec_boolean ("is-last", NULL, NULL, + FALSE, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + properties [PROP_SIZE] = + g_param_spec_uint ("size", NULL, NULL, + 0, G_MAXUINT16, 0, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + properties [PROP_PATH] = + g_param_spec_string ("path", NULL, NULL, + NULL, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); +} + +static void +sysprof_document_file_chunk_init (SysprofDocumentFileChunk *self) +{ +} + +gboolean +sysprof_document_file_chunk_get_is_last (SysprofDocumentFileChunk *self) +{ + const SysprofCaptureFileChunk *file_chunk; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_FILE_CHUNK (self), FALSE); + + file_chunk = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureFileChunk); + + return file_chunk->is_last; +} + +guint +sysprof_document_file_chunk_get_size (SysprofDocumentFileChunk *self) +{ + const SysprofCaptureFileChunk *file_chunk; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_FILE_CHUNK (self), FALSE); + + file_chunk = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureFileChunk); + + return SYSPROF_DOCUMENT_FRAME_UINT16 (self, file_chunk->len); +} + +const char * +sysprof_document_file_chunk_get_path (SysprofDocumentFileChunk *self) +{ + const SysprofCaptureFileChunk *file_chunk; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_FILE_CHUNK (self), FALSE); + + file_chunk = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureFileChunk); + + return SYSPROF_DOCUMENT_FRAME_CSTRING (self, file_chunk->path); +} + +const guint8 * +sysprof_document_file_chunk_get_data (SysprofDocumentFileChunk *self, + guint *size) +{ + const SysprofCaptureFileChunk *file_chunk; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_FILE_CHUNK (self), FALSE); + + file_chunk = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureFileChunk); + + if (size != NULL) + *size = sysprof_document_file_chunk_get_size (self); + + return file_chunk->data; +} diff --git a/src/libsysprof-analyze/sysprof-document-file-chunk.h b/src/libsysprof-analyze/sysprof-document-file-chunk.h new file mode 100644 index 00000000..caf7dd1c --- /dev/null +++ b/src/libsysprof-analyze/sysprof-document-file-chunk.h @@ -0,0 +1,49 @@ +/* sysprof-document-file-chunk.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-document-frame.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_DOCUMENT_FILE_CHUNK (sysprof_document_file_chunk_get_type()) +#define SYSPROF_IS_DOCUMENT_FILE_CHUNK(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_DOCUMENT_FILE_CHUNK) +#define SYSPROF_DOCUMENT_FILE_CHUNK(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_DOCUMENT_FILE_CHUNK, SysprofDocumentFileChunk) +#define SYSPROF_DOCUMENT_FILE_CHUNK_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_DOCUMENT_FILE_CHUNK, SysprofDocumentFileChunkClass) + +typedef struct _SysprofDocumentFileChunk SysprofDocumentFileChunk; +typedef struct _SysprofDocumentFileChunkClass SysprofDocumentFileChunkClass; + +SYSPROF_AVAILABLE_IN_ALL +GType sysprof_document_file_chunk_get_type (void) G_GNUC_CONST; +SYSPROF_AVAILABLE_IN_ALL +gboolean sysprof_document_file_chunk_get_is_last (SysprofDocumentFileChunk *self); +SYSPROF_AVAILABLE_IN_ALL +guint sysprof_document_file_chunk_get_size (SysprofDocumentFileChunk *self); +SYSPROF_AVAILABLE_IN_ALL +const char *sysprof_document_file_chunk_get_path (SysprofDocumentFileChunk *self); +SYSPROF_AVAILABLE_IN_ALL +const guint8 *sysprof_document_file_chunk_get_data (SysprofDocumentFileChunk *self, + guint *size); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofDocumentFileChunk, g_object_unref) + +G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-document-frame.c b/src/libsysprof-analyze/sysprof-document-frame.c index d9374ae2..21e7f1b2 100644 --- a/src/libsysprof-analyze/sysprof-document-frame.c +++ b/src/libsysprof-analyze/sysprof-document-frame.c @@ -24,6 +24,7 @@ #include "sysprof-document-allocation.h" #include "sysprof-document-exit.h" +#include "sysprof-document-file-chunk.h" #include "sysprof-document-fork.h" #include "sysprof-document-log.h" #include "sysprof-document-mark.h" @@ -163,6 +164,10 @@ _sysprof_document_frame_new (GMappedFile *mapped_file, gtype = SYSPROF_TYPE_DOCUMENT_ALLOCATION; break; + case SYSPROF_CAPTURE_FRAME_FILE_CHUNK: + gtype = SYSPROF_TYPE_DOCUMENT_FILE_CHUNK; + break; + default: gtype = SYSPROF_TYPE_DOCUMENT_FRAME; break; From e763cc14bb070069e6157ef9aba814710a39798e Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 5 May 2023 13:37:03 -0700 Subject: [PATCH 0064/1030] libsysprof-analyze: index locations of file chunks Create a roaring bitmap of list model positions that contain file chunks. We can use this in the future to avoid iterating the whole listmodel for matching file chunks to just the index positions containing file chunks. --- src/libsysprof-analyze/sysprof-document.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index 94eee0af..768e8486 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -37,6 +37,7 @@ struct _SysprofDocument GMappedFile *mapped_file; const guint8 *base; + GtkBitset *file_chunks; GtkBitset *traceables; GMutex strings_mutex; @@ -102,6 +103,7 @@ sysprof_document_finalize (GObject *object) g_clear_pointer (&self->frames, g_array_unref); g_clear_pointer (&self->strings, g_hash_table_unref); g_clear_pointer (&self->traceables, gtk_bitset_unref); + g_clear_pointer (&self->file_chunks, gtk_bitset_unref); g_mutex_clear (&self->strings_mutex); @@ -124,6 +126,7 @@ sysprof_document_init (SysprofDocument *self) self->strings = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify)g_ref_string_release); self->traceables = gtk_bitset_new_empty (); + self->file_chunks = gtk_bitset_new_empty (); } static gboolean @@ -181,6 +184,8 @@ sysprof_document_load (SysprofDocument *self, if (tainted->type == SYSPROF_CAPTURE_FRAME_SAMPLE || tainted->type == SYSPROF_CAPTURE_FRAME_ALLOCATION) gtk_bitset_add (self->traceables, self->frames->len); + else if (tainted->type == SYSPROF_CAPTURE_FRAME_FILE_CHUNK) + gtk_bitset_add (self->file_chunks, self->frames->len); pos += frame_len; From 83a61dd9ef557a74e83b1af721384fd74d1c0a11 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 5 May 2023 13:38:57 -0700 Subject: [PATCH 0065/1030] libsysprof-analyze: simplify sysprof_document_lookup_file_async() Instead of walking the listmodel for every object which might be a file chunk, and treating it as possibly non-native-endianness, this uses the new index of file chunks and accesses their frame as an object. That object will do endianness conversions for us. The extra overhead of creating the GObject is lessened by avoiding looking at many of the frames in the model. --- src/libsysprof-analyze/sysprof-document.c | 74 ++++++++--------------- 1 file changed, 25 insertions(+), 49 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index 768e8486..86b0a607 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -25,6 +25,7 @@ #include #include "sysprof-document-private.h" +#include "sysprof-document-file-chunk.h" #include "sysprof-document-frame-private.h" #include "sysprof-document-symbols-private.h" #include "sysprof-symbolizer-private.h" @@ -385,8 +386,8 @@ sysprof_document_lookup_file (GTask *task, SysprofDocument *self = source_object; g_autoptr(GByteArray) bytes = NULL; const char *filename = task_data; - gboolean is_native; - int filename_len; + GtkBitsetIter iter; + guint i; g_assert (G_IS_TASK (task)); g_assert (SYSPROF_IS_DOCUMENT (source_object)); @@ -394,17 +395,6 @@ sysprof_document_lookup_file (GTask *task, g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); bytes = g_byte_array_new (); - is_native = self->needs_swap == FALSE; - filename_len = strlen (filename); - - if (filename_len > 255) - { - g_task_return_new_error (task, - G_IO_ERROR, - G_IO_ERROR_INVALID_FILENAME, - "Filename too long for storage in capture file"); - return; - } /* We can access capture data on a thread because the pointers to * frames are created during construction and then never mutated. @@ -413,45 +403,31 @@ sysprof_document_lookup_file (GTask *task, * not need to swap frame->type becaues it's 1 byte. */ - for (guint i = 0; i < self->frames->len; i++) + if (gtk_bitset_iter_init_first (&iter, self->file_chunks, &i)) { - const SysprofDocumentFramePointer *ptr = &g_array_index (self->frames, SysprofDocumentFramePointer, i); - const SysprofCaptureFrame *frame = (gpointer)&self->base[ptr->offset]; - const SysprofCaptureFileChunk *chunk; - SysprofCaptureFrameType type = frame->type; - guint16 data_len; - - /* Ignore everything but file chunks */ - if (type != SYSPROF_CAPTURE_FRAME_FILE_CHUNK) - continue; - - chunk = (const SysprofCaptureFileChunk *)(gpointer)frame; - - /* Check path without being certain frame->path is \0 terminatd */ - if (memcmp (filename, chunk->path, filename_len) != 0 || - chunk->path[filename_len] != 0) - continue; - - if (is_native) - data_len = chunk->len; - else -#if G_BYTE_ORDER == G_LITTLE_ENDIAN - data_len = GUINT16_TO_LE (chunk->len); -#else - data_len = GUINT16_TO_BE (chunk->len); -#endif - - /* Check for corrupted file chunk data length */ - if (G_STRUCT_OFFSET (SysprofCaptureFileChunk, data) + data_len > ptr->length) + do { - g_byte_array_set_size (bytes, 0); - break; + g_autoptr(SysprofDocumentFileChunk) file_chunk = sysprof_document_get_item ((GListModel *)self, i); + const char *path; + + if (!SYSPROF_IS_DOCUMENT_FILE_CHUNK (file_chunk)) + continue; + + path = sysprof_document_file_chunk_get_path (file_chunk); + + if (g_strcmp0 (path, filename) == 0) + { + const guint8 *data = sysprof_document_file_chunk_get_data (file_chunk, NULL); + guint size = sysprof_document_file_chunk_get_size (file_chunk); + + g_byte_array_append (bytes, data, size); + + if (sysprof_document_file_chunk_get_is_last (file_chunk)) + break; + } + } - - g_byte_array_append (bytes, chunk->data, data_len); - - if (chunk->is_last) - break; + while (gtk_bitset_iter_next (&iter, &i)); } if (bytes->len == 0) From 634a32bdfa52d8728af6a962d8141881609026f3 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 5 May 2023 16:35:10 -0700 Subject: [PATCH 0066/1030] libsysprof-analyze: allow zero length embedded files --- src/libsysprof-analyze/sysprof-document.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index 86b0a607..41e0b837 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -387,6 +387,7 @@ sysprof_document_lookup_file (GTask *task, g_autoptr(GByteArray) bytes = NULL; const char *filename = task_data; GtkBitsetIter iter; + gboolean was_found = FALSE; guint i; g_assert (G_IS_TASK (task)); @@ -422,6 +423,8 @@ sysprof_document_lookup_file (GTask *task, g_byte_array_append (bytes, data, size); + was_found = TRUE; + if (sysprof_document_file_chunk_get_is_last (file_chunk)) break; } @@ -430,7 +433,7 @@ sysprof_document_lookup_file (GTask *task, while (gtk_bitset_iter_next (&iter, &i)); } - if (bytes->len == 0) + if (!was_found) g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, From e6ff4e838c510c60b98800de08c8195379df54e0 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 5 May 2023 16:35:16 -0700 Subject: [PATCH 0067/1030] libsysprof-analyze: print info about file chunks --- src/libsysprof-analyze/tests/test-capture-model.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libsysprof-analyze/tests/test-capture-model.c b/src/libsysprof-analyze/tests/test-capture-model.c index 7719525f..1e945528 100644 --- a/src/libsysprof-analyze/tests/test-capture-model.c +++ b/src/libsysprof-analyze/tests/test-capture-model.c @@ -58,6 +58,10 @@ main (int argc, g_print (" id=%s value=%s", sysprof_document_metadata_get_id (SYSPROF_DOCUMENT_METADATA (frame)), sysprof_document_metadata_get_value (SYSPROF_DOCUMENT_METADATA (frame))); + else if (SYSPROF_IS_DOCUMENT_FILE_CHUNK (frame)) + g_print (" file-chunk path=%s len=%u", + sysprof_document_file_chunk_get_path (SYSPROF_DOCUMENT_FILE_CHUNK (frame)), + sysprof_document_file_chunk_get_size (SYSPROF_DOCUMENT_FILE_CHUNK (frame))); else if (SYSPROF_IS_DOCUMENT_FORK (frame)) g_print (" child-pid=%d", sysprof_document_fork_get_child_pid (SYSPROF_DOCUMENT_FORK (frame))); From f5a97fa945e802ce0d3111f3547ab4ce74339ca2 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 5 May 2023 16:35:53 -0700 Subject: [PATCH 0068/1030] libsysprof-analyze: ignore zero length data --- src/libsysprof-analyze/sysprof-document.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index 41e0b837..4deeaba4 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -421,7 +421,8 @@ sysprof_document_lookup_file (GTask *task, const guint8 *data = sysprof_document_file_chunk_get_data (file_chunk, NULL); guint size = sysprof_document_file_chunk_get_size (file_chunk); - g_byte_array_append (bytes, data, size); + if (size > 0) + g_byte_array_append (bytes, data, size); was_found = TRUE; From 7720f690e0279763e60121e74424b631e624910b Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 8 May 2023 12:23:14 -0700 Subject: [PATCH 0069/1030] libsysprof-analyze: add bitset index This is meant to be a simple index helper where we already have a bitset created for a low-cardinality index. We can reuse the read-only bitset and filter on the document itself. This index will keep a reference on the document so it's lifetime persists. --- src/libsysprof-analyze/meson.build | 1 + .../sysprof-document-bitset-index-private.h | 35 ++++++ .../sysprof-document-bitset-index.c | 119 ++++++++++++++++++ 3 files changed, 155 insertions(+) create mode 100644 src/libsysprof-analyze/sysprof-document-bitset-index-private.h create mode 100644 src/libsysprof-analyze/sysprof-document-bitset-index.c diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build index 91b65de2..782685ee 100644 --- a/src/libsysprof-analyze/meson.build +++ b/src/libsysprof-analyze/meson.build @@ -21,6 +21,7 @@ libsysprof_analyze_public_sources = [ ] libsysprof_analyze_private_sources = [ + 'sysprof-document-bitset-index.c', 'sysprof-mapped-file.c', 'sysprof-memory-map.c', 'sysprof-mount-device.c', diff --git a/src/libsysprof-analyze/sysprof-document-bitset-index-private.h b/src/libsysprof-analyze/sysprof-document-bitset-index-private.h new file mode 100644 index 00000000..b541828c --- /dev/null +++ b/src/libsysprof-analyze/sysprof-document-bitset-index-private.h @@ -0,0 +1,35 @@ +/* sysprof-document-bitset-index.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include +#include + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_DOCUMENT_BITSET_INDEX (sysprof_document_bitset_index_get_type()) + +G_DECLARE_FINAL_TYPE (SysprofDocumentBitsetIndex, sysprof_document_bitset_index, SYSPROF, DOCUMENT_BITSET_INDEX, GObject) + +GListModel *_sysprof_document_bitset_index_new (GListModel *model, + GtkBitset *bitset); + +G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-document-bitset-index.c b/src/libsysprof-analyze/sysprof-document-bitset-index.c new file mode 100644 index 00000000..8f4b9496 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-document-bitset-index.c @@ -0,0 +1,119 @@ +/* sysprof-document-bitset-index.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-document-bitset-index-private.h" + +struct _SysprofDocumentBitsetIndex +{ + GObject parent_instance; + GListModel *model; + GtkBitset *bitset; +}; + +static GType +sysprof_document_bitset_index_get_item_type (GListModel *model) +{ + SysprofDocumentBitsetIndex *self = SYSPROF_DOCUMENT_BITSET_INDEX (model); + + if (self->model != NULL) + return g_list_model_get_item_type (self->model); + + return G_TYPE_OBJECT; +} + +static guint +sysprof_document_bitset_index_get_n_items (GListModel *model) +{ + SysprofDocumentBitsetIndex *self = SYSPROF_DOCUMENT_BITSET_INDEX (model); + + if (self->bitset != NULL) + return gtk_bitset_get_size (self->bitset); + + return 0; +} + +static gpointer +sysprof_document_bitset_index_get_item (GListModel *model, + guint position) +{ + SysprofDocumentBitsetIndex *self = SYSPROF_DOCUMENT_BITSET_INDEX (model); + + if (self->model == NULL || self->bitset == NULL) + return NULL; + + if (position >= gtk_bitset_get_size (self->bitset)) + return NULL; + + return g_list_model_get_item (self->model, + gtk_bitset_get_nth (self->bitset, position)); +} + +static void +list_model_iface_init (GListModelInterface *iface) +{ + iface->get_item = sysprof_document_bitset_index_get_item; + iface->get_item_type = sysprof_document_bitset_index_get_item_type; + iface->get_n_items = sysprof_document_bitset_index_get_n_items; +} + +G_DEFINE_FINAL_TYPE_WITH_CODE (SysprofDocumentBitsetIndex, sysprof_document_bitset_index, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init)) + +static void +sysprof_document_bitset_index_dispose (GObject *object) +{ + SysprofDocumentBitsetIndex *self = (SysprofDocumentBitsetIndex *)object; + + g_clear_pointer (&self->bitset, gtk_bitset_unref); + g_clear_object (&self->model); + + G_OBJECT_CLASS (sysprof_document_bitset_index_parent_class)->dispose (object); +} + +static void +sysprof_document_bitset_index_class_init (SysprofDocumentBitsetIndexClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = sysprof_document_bitset_index_dispose; +} + +static void +sysprof_document_bitset_index_init (SysprofDocumentBitsetIndex *self) +{ +} + +GListModel * +_sysprof_document_bitset_index_new (GListModel *model, + GtkBitset *bitset) +{ + SysprofDocumentBitsetIndex *self; + + g_return_val_if_fail (G_IS_LIST_MODEL (model), NULL); + g_return_val_if_fail (bitset != NULL, NULL); + + self = g_object_new (SYSPROF_TYPE_DOCUMENT_BITSET_INDEX, NULL); + self->model = g_object_ref (model); + self->bitset = gtk_bitset_ref (bitset); + + return G_LIST_MODEL (self); +} From c18b401ab6ef3b940e48ec216d7d253165c69ae3 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 8 May 2023 12:24:26 -0700 Subject: [PATCH 0070/1030] libsysprof-analyze: add listmodel of traceables It can be handy to iterate through the traceables and we already have a bitset index for that. Use the new bitset index listmodel to provide that filtered list externally without having to inflate every object in the underlying listmodel, as GtkFilterListModel would have to do. --- src/libsysprof-analyze/sysprof-document.c | 19 +++++++++++++++++++ src/libsysprof-analyze/sysprof-document.h | 2 ++ 2 files changed, 21 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index 4deeaba4..718be6b8 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -25,6 +25,8 @@ #include #include "sysprof-document-private.h" + +#include "sysprof-document-bitset-index-private.h" #include "sysprof-document-file-chunk.h" #include "sysprof-document-frame-private.h" #include "sysprof-document-symbols-private.h" @@ -495,3 +497,20 @@ _sysprof_document_traceables (SysprofDocument *self) return self->traceables; } + +/** + * sysprof_document_list_traceables: + * @self: a #SysprofDocument + * + * Gets a #GListModel containing #SysprofDocumentTraceable found within + * the #SysprofDocument. + * + * Returns: (transfer full): a #GListModel + */ +GListModel * +sysprof_document_list_traceables (SysprofDocument *self) +{ + g_return_val_if_fail (SYSPROF_IS_DOCUMENT (self), NULL); + + return _sysprof_document_bitset_index_new (G_LIST_MODEL (self), self->traceables); +} diff --git a/src/libsysprof-analyze/sysprof-document.h b/src/libsysprof-analyze/sysprof-document.h index fb62bdbb..c9afd003 100644 --- a/src/libsysprof-analyze/sysprof-document.h +++ b/src/libsysprof-analyze/sysprof-document.h @@ -60,5 +60,7 @@ SYSPROF_AVAILABLE_IN_ALL GBytes *sysprof_document_lookup_file_finish (SysprofDocument *self, GAsyncResult *result, GError **error); +SYSPROF_AVAILABLE_IN_ALL +GListModel *sysprof_document_list_traceables (SysprofDocument *self); G_END_DECLS From f2479912e6f05b73faccb50654557750afec4283 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 8 May 2023 12:24:44 -0700 Subject: [PATCH 0071/1030] libsysprof-analyze: cleanup some helper code for symbolize --- .../sysprof-document-symbols.c | 5 +--- src/libsysprof-analyze/tests/test-symbolize.c | 29 +++++++++++++++++++ 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-document-symbols.c b/src/libsysprof-analyze/sysprof-document-symbols.c index c008990d..6fddfedb 100644 --- a/src/libsysprof-analyze/sysprof-document-symbols.c +++ b/src/libsysprof-analyze/sysprof-document-symbols.c @@ -91,10 +91,7 @@ sysprof_document_symbols_add_traceable (SysprofDocumentSymbols *self, SysprofAddress address = addresses[i]; SysprofAddressContext context; - if (sysprof_address_is_context_switch (address, &context)) - g_print ("%d: %s\n", i, sysprof_address_context_to_string (context)); - else - g_print ("%d: %p\n", i, (gpointer)address); + /* TODO: */ } } diff --git a/src/libsysprof-analyze/tests/test-symbolize.c b/src/libsysprof-analyze/tests/test-symbolize.c index 62731995..6a8ef4e6 100644 --- a/src/libsysprof-analyze/tests/test-symbolize.c +++ b/src/libsysprof-analyze/tests/test-symbolize.c @@ -11,7 +11,9 @@ symbolize_cb (GObject *object, { SysprofDocument *document = (SysprofDocument *)object; g_autoptr(SysprofDocumentSymbols) symbols = NULL; + g_autoptr(GListModel) traceables = NULL; g_autoptr(GError) error = NULL; + guint n_items; g_assert (SYSPROF_IS_DOCUMENT (document)); g_assert (G_IS_ASYNC_RESULT (result)); @@ -19,6 +21,33 @@ symbolize_cb (GObject *object, if (!(symbols = sysprof_document_symbolize_finish (document, result, &error))) g_error ("Failed to symbolize: %s", error->message); + traceables = sysprof_document_list_traceables (document); + n_items = g_list_model_get_n_items (traceables); + + for (guint i = 0; i < n_items; i++) + { + g_autoptr(SysprofDocumentTraceable) traceable = g_list_model_get_item (traceables, i); + guint depth; + + g_assert (traceable != NULL); + g_assert (SYSPROF_IS_DOCUMENT_TRACEABLE (traceable)); + + depth = sysprof_document_traceable_get_stack_depth (traceable); + + g_print ("%s depth=%u\n", G_OBJECT_TYPE_NAME (traceable), depth); + + for (guint j = 0; j < depth; j++) + { + SysprofAddress address = sysprof_document_traceable_get_stack_address (traceable, j); + + g_print (" %02d: %p\n", j, GSIZE_TO_POINTER (address)); + + /* TODO: get symbol name from document symbols */ + } + + g_print (" ================\n"); + } + g_print ("Document symbolized\n"); g_main_loop_quit (main_loop); From 7f6191a3dd8ed24aab67c812225b22f91e3402a0 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 8 May 2023 14:00:09 -0700 Subject: [PATCH 0072/1030] libsysprof-analyze: add stub for symbol lookup --- .../sysprof-document-symbols.c | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-document-symbols.c b/src/libsysprof-analyze/sysprof-document-symbols.c index 6fddfedb..1d28c15f 100644 --- a/src/libsysprof-analyze/sysprof-document-symbols.c +++ b/src/libsysprof-analyze/sysprof-document-symbols.c @@ -92,6 +92,11 @@ sysprof_document_symbols_add_traceable (SysprofDocumentSymbols *self, SysprofAddressContext context; /* TODO: */ + + if (sysprof_address_is_context_switch (address, &context)) + { + last_context = context; + } } } @@ -171,3 +176,25 @@ _sysprof_document_symbols_new_finish (GAsyncResult *result, return g_task_propagate_pointer (G_TASK (result), error); } + +/** + * sysprof_document_symbols_lookup: + * @self: a #SysprofDocumentSymbols + * @pid: the process identifier + * @context: the #SysprofAddressContext for the address + * @address: a #SysprofAddress to lookup the symbol for + * + * Locates the symbol that is found at @address within @context of @pid. + * + * Returns: (transfer none) (nullable): a #SysprofSymbol or %NULL + */ +SysprofSymbol * +sysprof_document_symbols_lookup (SysprofDocumentSymbols *self, + int pid, + SysprofAddressContext context, + SysprofAddress address) +{ + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_SYMBOLS (self), NULL); + + return NULL; +} From dc57721699c1b7b960fe20fac507720982591f82 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 8 May 2023 14:01:17 -0700 Subject: [PATCH 0073/1030] libsysprof-analyze: add some dummy symbolize example We still need the document symbols to do the right thing, which we will get to after we have access to mount namespace and file loading. --- src/libsysprof-analyze/tests/test-symbolize.c | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/libsysprof-analyze/tests/test-symbolize.c b/src/libsysprof-analyze/tests/test-symbolize.c index 6a8ef4e6..5d7a8a21 100644 --- a/src/libsysprof-analyze/tests/test-symbolize.c +++ b/src/libsysprof-analyze/tests/test-symbolize.c @@ -27,22 +27,44 @@ symbolize_cb (GObject *object, for (guint i = 0; i < n_items; i++) { g_autoptr(SysprofDocumentTraceable) traceable = g_list_model_get_item (traceables, i); + SysprofAddressContext last_context; guint depth; + int pid; g_assert (traceable != NULL); g_assert (SYSPROF_IS_DOCUMENT_TRACEABLE (traceable)); + last_context = SYSPROF_ADDRESS_CONTEXT_NONE; depth = sysprof_document_traceable_get_stack_depth (traceable); + pid = sysprof_document_frame_get_pid (SYSPROF_DOCUMENT_FRAME (traceable)); g_print ("%s depth=%u\n", G_OBJECT_TYPE_NAME (traceable), depth); for (guint j = 0; j < depth; j++) { SysprofAddress address = sysprof_document_traceable_get_stack_address (traceable, j); + SysprofAddressContext context; - g_print (" %02d: %p\n", j, GSIZE_TO_POINTER (address)); + if (!sysprof_address_is_context_switch (address, &context)) + { + SysprofSymbol *symbol = sysprof_document_symbols_lookup (symbols, pid, last_context, address); + + if (symbol != NULL) + g_print (" %02d: %p: %s [%s]\n", + j, + GSIZE_TO_POINTER (address), + sysprof_symbol_get_name (symbol), + sysprof_symbol_get_binary_path (symbol)); + else + g_print (" %02d: %p\n", j, GSIZE_TO_POINTER (address)); + } + else + { + last_context = context; + g_print (" %02d: %s\n", + j, sysprof_address_context_to_string (context)); + } - /* TODO: get symbol name from document symbols */ } g_print (" ================\n"); From 7c57da24403033322792c810baff9ab564c97c26 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 8 May 2023 14:25:27 -0700 Subject: [PATCH 0074/1030] libsysprof-analyze: add build-id to SysprofDocumentMmap In anticipation of getting PERF_EVENT_MMAP2 events, assume we tack on the build-id in hex after the filename like "filename\0@build-id\0" so that we can use it when decoding frames. That will allow us to avoid doing inode checks (which breaks when moving files between computers anyway) as well as potentially supporting debuginfod in the future via build-id. --- .../sysprof-document-mmap.c | 41 ++++++++++++++++++- .../sysprof-document-mmap.h | 2 + 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/src/libsysprof-analyze/sysprof-document-mmap.c b/src/libsysprof-analyze/sysprof-document-mmap.c index c5a2f85c..50bc5351 100644 --- a/src/libsysprof-analyze/sysprof-document-mmap.c +++ b/src/libsysprof-analyze/sysprof-document-mmap.c @@ -36,6 +36,7 @@ struct _SysprofDocumentMmapClass enum { PROP_0, + PROP_BUILD_ID, PROP_END_ADDRESS, PROP_FILE, PROP_FILE_INODE, @@ -78,6 +79,10 @@ sysprof_document_mmap_get_property (GObject *object, g_value_set_uint64 (value, sysprof_document_mmap_get_file_inode (self)); break; + case PROP_BUILD_ID: + g_value_set_string (value, sysprof_document_mmap_get_build_id (self)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } @@ -115,6 +120,11 @@ sysprof_document_mmap_class_init (SysprofDocumentMmapClass *klass) 0, G_MAXUINT64, 0, (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + properties [PROP_BUILD_ID] = + g_param_spec_string ("build-id", NULL, NULL, + NULL, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_properties (object_class, N_PROPS, properties); } @@ -176,9 +186,38 @@ sysprof_document_mmap_get_file (SysprofDocumentMmap *self) { const SysprofCaptureMap *mmap; - g_return_val_if_fail (SYSPROF_IS_DOCUMENT_MMAP (self), 0); + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_MMAP (self), NULL); mmap = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureMap); return SYSPROF_DOCUMENT_FRAME_CSTRING (self, mmap->filename); } + +const char * +sysprof_document_mmap_get_build_id (SysprofDocumentMmap *self) +{ + const char *file; + const char *build_id; + gsize len; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_MMAP (self), NULL); + + if (!(file = sysprof_document_mmap_get_file (self))) + return NULL; + + /* The build-id may be tacked on after the filename if after the + * Nil byte we get '@'. SYSPROF_DOCUMENT_FRAME_CSTRING() will check + * for bounds so we can feed it a position we don't know is part + * of our frame or not. We expect "FILE\0@BUILD_ID_IN_HEX\0". + */ + + len = strlen (file); + + if (!(build_id = SYSPROF_DOCUMENT_FRAME_CSTRING (self, &file[len+1]))) + return NULL; + + if (build_id[0] != '@') + return NULL; + + return &build_id[1]; +} diff --git a/src/libsysprof-analyze/sysprof-document-mmap.h b/src/libsysprof-analyze/sysprof-document-mmap.h index 0bbe3deb..65451730 100644 --- a/src/libsysprof-analyze/sysprof-document-mmap.h +++ b/src/libsysprof-analyze/sysprof-document-mmap.h @@ -45,6 +45,8 @@ SYSPROF_AVAILABLE_IN_ALL guint64 sysprof_document_mmap_get_file_offset (SysprofDocumentMmap *self); SYSPROF_AVAILABLE_IN_ALL const char *sysprof_document_mmap_get_file (SysprofDocumentMmap *self); +SYSPROF_AVAILABLE_IN_ALL +const char *sysprof_document_mmap_get_build_id (SysprofDocumentMmap *self); G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofDocumentMmap, g_object_unref) From 6daff5ac6edda062345a30d1430dc155e8c05e39 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 8 May 2023 15:49:42 -0700 Subject: [PATCH 0075/1030] libsysprof-analyze: return symbols for context switches --- .../sysprof-document-symbols.c | 47 +++++++++++++++++-- src/libsysprof-analyze/tests/test-symbolize.c | 28 ++++------- 2 files changed, 54 insertions(+), 21 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-document-symbols.c b/src/libsysprof-analyze/sysprof-document-symbols.c index 1d28c15f..25ebd3c8 100644 --- a/src/libsysprof-analyze/sysprof-document-symbols.c +++ b/src/libsysprof-analyze/sysprof-document-symbols.c @@ -23,11 +23,14 @@ #include "sysprof-document-private.h" #include "sysprof-document-symbols-private.h" #include "sysprof-document-traceable.h" +#include "sysprof-symbol-private.h" #include "sysprof-symbolizer-private.h" struct _SysprofDocumentSymbols { GObject parent_instance; + + SysprofSymbol *context_switches[SYSPROF_ADDRESS_CONTEXT_GUEST_USER+1]; }; G_DEFINE_FINAL_TYPE (SysprofDocumentSymbols, sysprof_document_symbols, G_TYPE_OBJECT) @@ -35,6 +38,11 @@ G_DEFINE_FINAL_TYPE (SysprofDocumentSymbols, sysprof_document_symbols, G_TYPE_OB static void sysprof_document_symbols_finalize (GObject *object) { + SysprofDocumentSymbols *self = (SysprofDocumentSymbols *)object; + + for (guint i = 0; i < G_N_ELEMENTS (self->context_switches); i++) + g_clear_object (&self->context_switches[i]); + G_OBJECT_CLASS (sysprof_document_symbols_parent_class)->finalize (object); } @@ -89,14 +97,15 @@ sysprof_document_symbols_add_traceable (SysprofDocumentSymbols *self, for (guint i = 0; i < n_addresses; i++) { SysprofAddress address = addresses[i]; - SysprofAddressContext context; - - /* TODO: */ + SysprofAddressContext context = SYSPROF_ADDRESS_CONTEXT_NONE; if (sysprof_address_is_context_switch (address, &context)) { last_context = context; } + else + { + } } } @@ -106,6 +115,17 @@ sysprof_document_symbols_worker (GTask *task, gpointer task_data, GCancellable *cancellable) { + static const struct { + const char *name; + guint value; + } context_switches[] = { + { "- - Hypervisor - -", SYSPROF_ADDRESS_CONTEXT_HYPERVISOR }, + { "- - Kernel - -", SYSPROF_ADDRESS_CONTEXT_KERNEL }, + { "- - User - -", SYSPROF_ADDRESS_CONTEXT_USER }, + { "- - Guest - -", SYSPROF_ADDRESS_CONTEXT_GUEST }, + { "- - Guest Kernel - -", SYSPROF_ADDRESS_CONTEXT_GUEST_KERNEL }, + { "- - Guest User - -", SYSPROF_ADDRESS_CONTEXT_GUEST_USER }, + }; Symbolize *state = task_data; GtkBitsetIter iter; GtkBitset *bitset; @@ -123,6 +143,21 @@ sysprof_document_symbols_worker (GTask *task, bitset = _sysprof_document_traceables (state->document); model = G_LIST_MODEL (state->document); + /* Create static symbols for context switch use */ + for (guint cs = 0; cs < G_N_ELEMENTS (context_switches); cs++) + { + g_autoptr(GRefString) name = g_ref_string_new_intern (context_switches[cs].name); + g_autoptr(SysprofSymbol) symbol = _sysprof_symbol_new (name, NULL, NULL); + + /* TODO: It would be nice if we had enough insight from the capture header + * as to the host system, so we can show "vmlinuz" and "Linux" respectively + * for binary-path and binary-nick when the capture came from Linux. + */ + + state->symbols->context_switches[context_switches[cs].value] = g_steal_pointer (&symbol); + } + + /* Walk through the available traceables which need symbols extracted */ if (gtk_bitset_iter_init_first (&iter, bitset, &i)) { do @@ -194,7 +229,13 @@ sysprof_document_symbols_lookup (SysprofDocumentSymbols *self, SysprofAddressContext context, SysprofAddress address) { + SysprofAddressContext new_context; + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_SYMBOLS (self), NULL); + g_return_val_if_fail (context <= SYSPROF_ADDRESS_CONTEXT_GUEST_USER, NULL); + + if (sysprof_address_is_context_switch (address, &new_context)) + return self->context_switches[new_context]; return NULL; } diff --git a/src/libsysprof-analyze/tests/test-symbolize.c b/src/libsysprof-analyze/tests/test-symbolize.c index 5d7a8a21..d1ea5761 100644 --- a/src/libsysprof-analyze/tests/test-symbolize.c +++ b/src/libsysprof-analyze/tests/test-symbolize.c @@ -43,28 +43,20 @@ symbolize_cb (GObject *object, for (guint j = 0; j < depth; j++) { SysprofAddress address = sysprof_document_traceable_get_stack_address (traceable, j); + SysprofSymbol *symbol = sysprof_document_symbols_lookup (symbols, pid, last_context, address); SysprofAddressContext context; - if (!sysprof_address_is_context_switch (address, &context)) - { - SysprofSymbol *symbol = sysprof_document_symbols_lookup (symbols, pid, last_context, address); + if (sysprof_address_is_context_switch (address, &context)) + last_context = context; - if (symbol != NULL) - g_print (" %02d: %p: %s [%s]\n", - j, - GSIZE_TO_POINTER (address), - sysprof_symbol_get_name (symbol), - sysprof_symbol_get_binary_path (symbol)); - else - g_print (" %02d: %p\n", j, GSIZE_TO_POINTER (address)); - } + if (symbol != NULL) + g_print (" %02d: %p: %s [%s]\n", + j, + GSIZE_TO_POINTER (address), + sysprof_symbol_get_name (symbol), + sysprof_symbol_get_binary_path (symbol) ?: ""); else - { - last_context = context; - g_print (" %02d: %s\n", - j, sysprof_address_context_to_string (context)); - } - + g_print (" %02d: %p\n", j, GSIZE_TO_POINTER (address)); } g_print (" ================\n"); From 689599ef4336a94426c287b8fdc3a7553b2ba190 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 8 May 2023 16:16:09 -0700 Subject: [PATCH 0076/1030] libsysprof-analyze: add note about symbolizing work --- src/libsysprof-analyze/sysprof-document-symbols.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-document-symbols.c b/src/libsysprof-analyze/sysprof-document-symbols.c index 25ebd3c8..16890562 100644 --- a/src/libsysprof-analyze/sysprof-document-symbols.c +++ b/src/libsysprof-analyze/sysprof-document-symbols.c @@ -88,6 +88,20 @@ sysprof_document_symbols_add_traceable (SysprofDocumentSymbols *self, g_assert (SYSPROF_IS_DOCUMENT_TRACEABLE (traceable)); g_assert (SYSPROF_IS_SYMBOLIZER (symbolizer)); + /* TODO: We need to get the SysprofMountNamespace for the PID which must have + * already been compiled. We also need the list SysprofDocumentMmap so that we + * can get the build-id or inode to do various validation checks. + * + * It would be nice if that was all pre-compiled by time we get here and also + * re-usable on the SysprofDocument for other tooling that may need to access + * that. + * + * The symbolizer will need to gain API to use those too. + * + * Some of this will need to be done asynchronously as well because decoding + * from debuginfod could be quite costly in disk/network/time. + */ + n_addresses = sysprof_document_traceable_get_stack_depth (traceable); addresses = g_alloca (sizeof (guint64) * n_addresses); sysprof_document_traceable_get_stack_addresses (traceable, addresses, n_addresses); From 9fe0ae53063631cf3d09cbdc0f9672937d78bdc8 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 9 May 2023 12:22:34 -0700 Subject: [PATCH 0077/1030] libsysprof-analyze: add type to represent an embedded file We have "chunks" within the capture file, this object will represent the collection of those chunks which is easier for applications to deal with if they want to view the contents. --- src/libsysprof-analyze/meson.build | 2 + src/libsysprof-analyze/sysprof-analyze.h | 1 + .../sysprof-document-file-private.h | 30 ++++ .../sysprof-document-file.c | 157 ++++++++++++++++++ .../sysprof-document-file.h | 39 +++++ 5 files changed, 229 insertions(+) create mode 100644 src/libsysprof-analyze/sysprof-document-file-private.h create mode 100644 src/libsysprof-analyze/sysprof-document-file.c create mode 100644 src/libsysprof-analyze/sysprof-document-file.h diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build index 782685ee..0939bcf2 100644 --- a/src/libsysprof-analyze/meson.build +++ b/src/libsysprof-analyze/meson.build @@ -3,6 +3,7 @@ libsysprof_analyze_public_sources = [ 'sysprof-document.c', 'sysprof-document-allocation.c', 'sysprof-document-exit.c', + 'sysprof-document-file.c', 'sysprof-document-file-chunk.c', 'sysprof-document-fork.c', 'sysprof-document-frame.c', @@ -34,6 +35,7 @@ libsysprof_analyze_public_headers = [ 'sysprof-document.h', 'sysprof-document-allocation.h', 'sysprof-document-exit.h', + 'sysprof-document-file.h', 'sysprof-document-file-chunk.h', 'sysprof-document-fork.h', 'sysprof-document-frame.h', diff --git a/src/libsysprof-analyze/sysprof-analyze.h b/src/libsysprof-analyze/sysprof-analyze.h index ba5180dc..f81e185d 100644 --- a/src/libsysprof-analyze/sysprof-analyze.h +++ b/src/libsysprof-analyze/sysprof-analyze.h @@ -29,6 +29,7 @@ G_BEGIN_DECLS # include "sysprof-document.h" # include "sysprof-document-allocation.h" # include "sysprof-document-exit.h" +# include "sysprof-document-file.h" # include "sysprof-document-file-chunk.h" # include "sysprof-document-fork.h" # include "sysprof-document-frame.h" diff --git a/src/libsysprof-analyze/sysprof-document-file-private.h b/src/libsysprof-analyze/sysprof-document-file-private.h new file mode 100644 index 00000000..c904124d --- /dev/null +++ b/src/libsysprof-analyze/sysprof-document-file-private.h @@ -0,0 +1,30 @@ +/* sysprof-document-file-private.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-document-file.h" + +G_BEGIN_DECLS + +SysprofDocumentFile *_sysprof_document_file_new (const char *path, + GPtrArray *file_chunks); + +G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-document-file.c b/src/libsysprof-analyze/sysprof-document-file.c new file mode 100644 index 00000000..56e7a00b --- /dev/null +++ b/src/libsysprof-analyze/sysprof-document-file.c @@ -0,0 +1,157 @@ +/* sysprof-document-file.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-document-file-chunk.h" +#include "sysprof-document-file-private.h" + +struct _SysprofDocumentFile +{ + GObject parent_instance; + char *path; + GPtrArray *file_chunks; +}; + +enum { + PROP_0, + PROP_BYTES, + PROP_PATH, + N_PROPS +}; + +G_DEFINE_FINAL_TYPE (SysprofDocumentFile, sysprof_document_file, G_TYPE_OBJECT) + +static GParamSpec *properties [N_PROPS]; + +static void +sysprof_document_file_finalize (GObject *object) +{ + SysprofDocumentFile *self = (SysprofDocumentFile *)object; + + g_clear_pointer (&self->path, g_free); + g_clear_pointer (&self->file_chunks, g_ptr_array_unref); + + G_OBJECT_CLASS (sysprof_document_file_parent_class)->finalize (object); +} + +static void +sysprof_document_file_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofDocumentFile *self = SYSPROF_DOCUMENT_FILE (object); + + switch (prop_id) + { + case PROP_PATH: + g_value_set_string (value, sysprof_document_file_get_path (self)); + break; + + case PROP_BYTES: + g_value_take_boxed (value, sysprof_document_file_dup_bytes (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_document_file_class_init (SysprofDocumentFileClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = sysprof_document_file_finalize; + object_class->get_property = sysprof_document_file_get_property; + + properties [PROP_PATH] = + g_param_spec_string ("path", NULL, NULL, + NULL, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + properties [PROP_BYTES] = + g_param_spec_boxed ("bytes", NULL, NULL, + G_TYPE_BYTES, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); +} + +static void +sysprof_document_file_init (SysprofDocumentFile *self) +{ +} + +SysprofDocumentFile * +_sysprof_document_file_new (const char *path, + GPtrArray *file_chunks) +{ + SysprofDocumentFile *self; + + g_return_val_if_fail (path != NULL, NULL); + g_return_val_if_fail (file_chunks != NULL, NULL); + + self = g_object_new (SYSPROF_TYPE_DOCUMENT_FILE, NULL); + self->path = g_strdup (path); + self->file_chunks = file_chunks; + + return self; +} + +const char * +sysprof_document_file_get_path (SysprofDocumentFile *self) +{ + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_FILE (self), NULL); + + return self->path; +} + +/** + * sysprof_document_file_dup_contents: + * @self: a #SysprofDocumentFile + * + * Creates a new #GBytes containing the contents of the file. + * + * Returns: (transfer full): a #GBytes + */ +GBytes * +sysprof_document_file_dup_bytes (SysprofDocumentFile *self) +{ + GArray *ar; + guint len; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_FILE (self), NULL); + + ar = g_array_new (TRUE, FALSE, sizeof (char)); + + for (guint i = 0; i < self->file_chunks->len; i++) + { + SysprofDocumentFileChunk *file_chunk = g_ptr_array_index (self->file_chunks, i); + const guint8 *data = sysprof_document_file_chunk_get_data (file_chunk, &len); + + g_array_append_vals (ar, data, len); + } + + len = ar->len; + + return g_bytes_new_take (g_array_free (ar, FALSE), len); +} diff --git a/src/libsysprof-analyze/sysprof-document-file.h b/src/libsysprof-analyze/sysprof-document-file.h new file mode 100644 index 00000000..0b66501d --- /dev/null +++ b/src/libsysprof-analyze/sysprof-document-file.h @@ -0,0 +1,39 @@ +/* sysprof-document-file.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +#include + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_DOCUMENT_FILE (sysprof_document_file_get_type()) + +SYSPROF_AVAILABLE_IN_ALL +G_DECLARE_FINAL_TYPE (SysprofDocumentFile, sysprof_document_file, SYSPROF, DOCUMENT_FILE, GObject) + +SYSPROF_AVAILABLE_IN_ALL +const char *sysprof_document_file_get_path (SysprofDocumentFile *self); +SYSPROF_AVAILABLE_IN_ALL +GBytes *sysprof_document_file_dup_bytes (SysprofDocumentFile *self); + +G_END_DECLS From 938a1bbb4ac011a2aaa393407bf840f4a9f65734 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 9 May 2023 12:24:14 -0700 Subject: [PATCH 0078/1030] libsysprof-analyze: add listmodel of embedded files This also indexes the first position of a file by filename so that we can skip items in the capture file. Generally, embedded files are a single frame so that will only be one frame to look at. But even when it is a few frames, they are generally sequential so this vastly reduces how many frames we'll need to look at for files. --- src/libsysprof-analyze/sysprof-document.c | 109 ++++++++++++++++-- src/libsysprof-analyze/sysprof-document.h | 2 + src/libsysprof-analyze/tests/meson.build | 1 + .../tests/test-list-files.c | 55 +++++++++ 4 files changed, 159 insertions(+), 8 deletions(-) create mode 100644 src/libsysprof-analyze/tests/test-list-files.c diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index 718be6b8..5958b2aa 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -28,6 +28,7 @@ #include "sysprof-document-bitset-index-private.h" #include "sysprof-document-file-chunk.h" +#include "sysprof-document-file-private.h" #include "sysprof-document-frame-private.h" #include "sysprof-document-symbols-private.h" #include "sysprof-symbolizer-private.h" @@ -43,6 +44,8 @@ struct _SysprofDocument GtkBitset *file_chunks; GtkBitset *traceables; + GHashTable *files_first_position; + GMutex strings_mutex; GHashTable *strings; @@ -97,6 +100,14 @@ list_model_iface_init (GListModelInterface *iface) G_DEFINE_FINAL_TYPE_WITH_CODE (SysprofDocument, sysprof_document, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init)) +GtkBitset * +_sysprof_document_traceables (SysprofDocument *self) +{ + g_return_val_if_fail (SYSPROF_IS_DOCUMENT (self), NULL); + + return self->traceables; +} + static void sysprof_document_finalize (GObject *object) { @@ -107,6 +118,7 @@ sysprof_document_finalize (GObject *object) g_clear_pointer (&self->strings, g_hash_table_unref); g_clear_pointer (&self->traceables, gtk_bitset_unref); g_clear_pointer (&self->file_chunks, gtk_bitset_unref); + g_clear_pointer (&self->files_first_position, g_hash_table_unref); g_mutex_clear (&self->strings_mutex); @@ -130,6 +142,21 @@ sysprof_document_init (SysprofDocument *self) (GDestroyNotify)g_ref_string_release); self->traceables = gtk_bitset_new_empty (); self->file_chunks = gtk_bitset_new_empty (); + + self->files_first_position = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); +} + +static inline gboolean +has_null_byte (const char *str, + const char *endptr) +{ + for (const char *c = str; c < endptr; c++) + { + if (*c == '\0') + return TRUE; + } + + return FALSE; } static gboolean @@ -137,6 +164,7 @@ sysprof_document_load (SysprofDocument *self, int capture_fd, GError **error) { + g_autoptr(GHashTable) files = NULL; goffset pos; gsize len; @@ -188,7 +216,17 @@ sysprof_document_load (SysprofDocument *self, tainted->type == SYSPROF_CAPTURE_FRAME_ALLOCATION) gtk_bitset_add (self->traceables, self->frames->len); else if (tainted->type == SYSPROF_CAPTURE_FRAME_FILE_CHUNK) - gtk_bitset_add (self->file_chunks, self->frames->len); + { + const SysprofCaptureFileChunk *file_chunk = (const SysprofCaptureFileChunk *)tainted; + + gtk_bitset_add (self->file_chunks, self->frames->len); + + if (has_null_byte (file_chunk->path, (const char *)file_chunk->data) && + !g_hash_table_contains (self->files_first_position, file_chunk->path)) + g_hash_table_insert (self->files_first_position, + g_strdup (file_chunk->path), + GUINT_TO_POINTER (self->frames->len)); + } pos += frame_len; @@ -389,7 +427,7 @@ sysprof_document_lookup_file (GTask *task, g_autoptr(GByteArray) bytes = NULL; const char *filename = task_data; GtkBitsetIter iter; - gboolean was_found = FALSE; + guint target; guint i; g_assert (G_IS_TASK (task)); @@ -398,6 +436,7 @@ sysprof_document_lookup_file (GTask *task, g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); bytes = g_byte_array_new (); + target = GPOINTER_TO_UINT (g_hash_table_lookup (self->files_first_position, filename)); /* We can access capture data on a thread because the pointers to * frames are created during construction and then never mutated. @@ -406,7 +445,7 @@ sysprof_document_lookup_file (GTask *task, * not need to swap frame->type becaues it's 1 byte. */ - if (gtk_bitset_iter_init_first (&iter, self->file_chunks, &i)) + if (gtk_bitset_iter_init_at (&iter, self->file_chunks, target, &i)) { do { @@ -431,7 +470,6 @@ sysprof_document_lookup_file (GTask *task, if (sysprof_document_file_chunk_get_is_last (file_chunk)) break; } - } while (gtk_bitset_iter_next (&iter, &i)); } @@ -463,7 +501,14 @@ sysprof_document_lookup_file_async (SysprofDocument *self, task = g_task_new (self, cancellable, callback, user_data); g_task_set_source_tag (task, sysprof_document_lookup_file_async); g_task_set_task_data (task, g_strdup (filename), g_free); - g_task_run_in_thread (task, sysprof_document_lookup_file); + + if (!g_hash_table_contains (self->files_first_position, filename)) + g_task_return_new_error (task, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "Filename could not be found"); + else + g_task_run_in_thread (task, sysprof_document_lookup_file); } /** @@ -490,12 +535,60 @@ sysprof_document_lookup_file_finish (SysprofDocument *self, return g_task_propagate_pointer (G_TASK (result), error); } -GtkBitset * -_sysprof_document_traceables (SysprofDocument *self) +/** + * sysprof_document_list_files: + * @self: a #SysprofDocument + * + * Gets a #GListModel of #SysprofDocumentFile + * + * Returns: (transfer full): a #GListModel + */ +GListModel * +sysprof_document_list_files (SysprofDocument *self) { + GHashTableIter hiter; + GtkBitsetIter iter; + GListStore *model; + gpointer key, value; + g_return_val_if_fail (SYSPROF_IS_DOCUMENT (self), NULL); - return self->traceables; + model = g_list_store_new (SYSPROF_TYPE_DOCUMENT_FILE); + + g_hash_table_iter_init (&hiter, self->files_first_position); + while (g_hash_table_iter_next (&hiter, &key, &value)) + { + g_autoptr(SysprofDocumentFile) file = NULL; + g_autoptr(GPtrArray) file_chunks = g_ptr_array_new_with_free_func (g_object_unref); + const char *path = key; + guint target = GPOINTER_TO_SIZE (value); + guint i; + + if (gtk_bitset_iter_init_at (&iter, self->file_chunks, target, &i)) + { + do + { + g_autoptr(SysprofDocumentFileChunk) file_chunk = sysprof_document_get_item ((GListModel *)self, i); + + if (g_strcmp0 (path, sysprof_document_file_chunk_get_path (file_chunk)) != 0) + { + gboolean is_last = sysprof_document_file_chunk_get_is_last (file_chunk); + + g_ptr_array_add (file_chunks, g_steal_pointer (&file_chunk)); + + if (is_last) + break; + } + } + while (gtk_bitset_iter_next (&iter, &i)); + } + + file = _sysprof_document_file_new (path, g_steal_pointer (&file_chunks)); + + g_list_store_append (model, file); + } + + return G_LIST_MODEL (model); } /** diff --git a/src/libsysprof-analyze/sysprof-document.h b/src/libsysprof-analyze/sysprof-document.h index c9afd003..efe09c37 100644 --- a/src/libsysprof-analyze/sysprof-document.h +++ b/src/libsysprof-analyze/sysprof-document.h @@ -51,6 +51,8 @@ SysprofDocumentSymbols *sysprof_document_symbolize_finish (SysprofDocument GAsyncResult *result, GError **error); SYSPROF_AVAILABLE_IN_ALL +GListModel *sysprof_document_list_files (SysprofDocument *self); +SYSPROF_AVAILABLE_IN_ALL void sysprof_document_lookup_file_async (SysprofDocument *self, const char *filename, GCancellable *cancellable, diff --git a/src/libsysprof-analyze/tests/meson.build b/src/libsysprof-analyze/tests/meson.build index e723a994..c328c977 100644 --- a/src/libsysprof-analyze/tests/meson.build +++ b/src/libsysprof-analyze/tests/meson.build @@ -13,6 +13,7 @@ libsysprof_analyze_testsuite_c_args = [ libsysprof_analyze_testsuite = { 'test-capture-model' : {'skip': true}, + 'test-list-files' : {'skip': true}, 'test-symbolize' : {'skip': true}, } diff --git a/src/libsysprof-analyze/tests/test-list-files.c b/src/libsysprof-analyze/tests/test-list-files.c new file mode 100644 index 00000000..bd2e177c --- /dev/null +++ b/src/libsysprof-analyze/tests/test-list-files.c @@ -0,0 +1,55 @@ +/* test-list-files.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include + +int +main (int argc, + char *argv[]) +{ + g_autoptr(SysprofDocument) document = NULL; + g_autoptr(GListModel) files = NULL; + g_autoptr(GError) error = NULL; + guint n_items; + + if (argc < 2) + { + g_printerr ("usage: %s CAPTURE_FILE\n", argv[0]); + return 1; + } + + if (!(document = sysprof_document_new (argv[1], &error))) + { + g_printerr ("Failed to open capture: %s\n", error->message); + return 1; + } + + files = sysprof_document_list_files (document); + n_items = g_list_model_get_n_items (files); + + for (guint i = 0; i < n_items; i++) + { + g_autoptr(SysprofDocumentFile) file = g_list_model_get_item (files, i); + + g_print ("%s\n", sysprof_document_file_get_path (file)); + } + + return 0; +} From e0e1791b2d675f603880c9817bc0701951739254 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 9 May 2023 12:36:59 -0700 Subject: [PATCH 0079/1030] libsysprof-analyze: make lookup_file synchronous Now that we have a lot of this indexed, it's fast enough to do on the calling thread. # Conflicts: # src/libsysprof-analyze/sysprof-document.c # src/libsysprof-analyze/sysprof-document.h --- .../sysprof-bundled-symbolizer.c | 38 ++--- src/libsysprof-analyze/sysprof-document.c | 141 +++++------------- src/libsysprof-analyze/sysprof-document.h | 14 +- 3 files changed, 50 insertions(+), 143 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-bundled-symbolizer.c b/src/libsysprof-analyze/sysprof-bundled-symbolizer.c index f0d4dbc0..cbe8e239 100644 --- a/src/libsysprof-analyze/sysprof-bundled-symbolizer.c +++ b/src/libsysprof-analyze/sysprof-bundled-symbolizer.c @@ -46,28 +46,6 @@ sysprof_bundled_symbolizer_decode (SysprofBundledSymbolizer *self, } -static void -sysprof_bundled_symbolizer_prepare_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - SysprofDocument *document = (SysprofDocument *)object; - g_autoptr(GTask) task = user_data; - g_autoptr(GError) error = NULL; - g_autoptr(GBytes) bytes = NULL; - - g_assert (SYSPROF_IS_DOCUMENT (document)); - g_assert (G_IS_ASYNC_RESULT (result)); - g_assert (G_IS_TASK (task)); - - if ((bytes = sysprof_document_lookup_file_finish (document, result, &error))) - sysprof_bundled_symbolizer_decode (g_task_get_source_object (task), - bytes, - _sysprof_document_is_native (document)); - - g_task_return_boolean (task, TRUE); -} - static void sysprof_bundled_symbolizer_prepare_async (SysprofSymbolizer *symbolizer, SysprofDocument *document, @@ -75,21 +53,23 @@ sysprof_bundled_symbolizer_prepare_async (SysprofSymbolizer *symbolizer, GAsyncReadyCallback callback, gpointer user_data) { + SysprofBundledSymbolizer *self = (SysprofBundledSymbolizer *)symbolizer; + g_autoptr(SysprofDocumentFile) file = NULL; + g_autoptr(GBytes) bytes = NULL; g_autoptr(GTask) task = NULL; - g_assert (SYSPROF_IS_BUNDLED_SYMBOLIZER (symbolizer)); + g_assert (SYSPROF_IS_BUNDLED_SYMBOLIZER (self)); g_assert (SYSPROF_IS_DOCUMENT (document)); g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); - task = g_task_new (symbolizer, cancellable, callback, user_data); + task = g_task_new (self, cancellable, callback, user_data); g_task_set_source_tag (task, sysprof_bundled_symbolizer_prepare_async); - sysprof_document_lookup_file_async (document, - "__symbols__", - cancellable, - sysprof_bundled_symbolizer_prepare_cb, - g_steal_pointer (&task)); + if ((file = sysprof_document_lookup_file (document, "__symbols__")) && + (bytes = sysprof_document_file_dup_bytes (file))) + sysprof_bundled_symbolizer_decode (self, bytes, _sysprof_document_is_native (document)); + g_task_return_boolean (task, TRUE); } static gboolean diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index 5958b2aa..5771daa9 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -417,122 +417,55 @@ _sysprof_document_is_native (SysprofDocument *self) return self->needs_swap == FALSE; } -static void -sysprof_document_lookup_file (GTask *task, - gpointer source_object, - gpointer task_data, - GCancellable *cancellable) +/** + * sysprof_document_lookup_file: + * @self: a #SysprofDocument + * @path: the path of the file + * + * Locates @path within the document and returns a #SysprofDocumentFile if + * it was found which can be used to access the contents. + * + * Returns: (transfer full) (nullable): a #SysprofDocumentFile + */ +SysprofDocumentFile * +sysprof_document_lookup_file (SysprofDocument *self, + const char *path) { - SysprofDocument *self = source_object; - g_autoptr(GByteArray) bytes = NULL; - const char *filename = task_data; - GtkBitsetIter iter; - guint target; - guint i; + gpointer key, value; - g_assert (G_IS_TASK (task)); - g_assert (SYSPROF_IS_DOCUMENT (source_object)); - g_assert (filename != NULL); - g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); + g_return_val_if_fail (SYSPROF_IS_DOCUMENT (self), NULL); + g_return_val_if_fail (path != NULL, NULL); - bytes = g_byte_array_new (); - target = GPOINTER_TO_UINT (g_hash_table_lookup (self->files_first_position, filename)); - - /* We can access capture data on a thread because the pointers to - * frames are created during construction and then never mutated. - * - * But do remember that frame data may not be byte-swapped. We do - * not need to swap frame->type becaues it's 1 byte. - */ - - if (gtk_bitset_iter_init_at (&iter, self->file_chunks, target, &i)) + if (g_hash_table_lookup_extended (self->files_first_position, path, &key, &value)) { - do + g_autoptr(GPtrArray) file_chunks = g_ptr_array_new_with_free_func (g_object_unref); + GtkBitsetIter iter; + guint target = GPOINTER_TO_SIZE (value); + guint i; + + if (gtk_bitset_iter_init_at (&iter, self->file_chunks, target, &i)) { - g_autoptr(SysprofDocumentFileChunk) file_chunk = sysprof_document_get_item ((GListModel *)self, i); - const char *path; - - if (!SYSPROF_IS_DOCUMENT_FILE_CHUNK (file_chunk)) - continue; - - path = sysprof_document_file_chunk_get_path (file_chunk); - - if (g_strcmp0 (path, filename) == 0) + do { - const guint8 *data = sysprof_document_file_chunk_get_data (file_chunk, NULL); - guint size = sysprof_document_file_chunk_get_size (file_chunk); + g_autoptr(SysprofDocumentFileChunk) file_chunk = sysprof_document_get_item ((GListModel *)self, i); - if (size > 0) - g_byte_array_append (bytes, data, size); + if (g_strcmp0 (path, sysprof_document_file_chunk_get_path (file_chunk)) == 0) + { + gboolean is_last = sysprof_document_file_chunk_get_is_last (file_chunk); - was_found = TRUE; + g_ptr_array_add (file_chunks, g_steal_pointer (&file_chunk)); - if (sysprof_document_file_chunk_get_is_last (file_chunk)) - break; + if (is_last) + break; + } } + while (gtk_bitset_iter_next (&iter, &i)); } - while (gtk_bitset_iter_next (&iter, &i)); + + return _sysprof_document_file_new (path, g_steal_pointer (&file_chunks)); } - if (!was_found) - g_task_return_new_error (task, - G_IO_ERROR, - G_IO_ERROR_NOT_FOUND, - "Failed to locate file \"%s\"", filename); - else - g_task_return_pointer (task, - g_byte_array_free_to_bytes (g_steal_pointer (&bytes)), - (GDestroyNotify)g_bytes_unref); -} - -void -sysprof_document_lookup_file_async (SysprofDocument *self, - const char *filename, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - g_autoptr(GTask) task = NULL; - - g_return_if_fail (SYSPROF_IS_DOCUMENT (self)); - g_return_if_fail (filename != NULL); - g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); - - task = g_task_new (self, cancellable, callback, user_data); - g_task_set_source_tag (task, sysprof_document_lookup_file_async); - g_task_set_task_data (task, g_strdup (filename), g_free); - - if (!g_hash_table_contains (self->files_first_position, filename)) - g_task_return_new_error (task, - G_IO_ERROR, - G_IO_ERROR_NOT_FOUND, - "Filename could not be found"); - else - g_task_run_in_thread (task, sysprof_document_lookup_file); -} - -/** - * sysprof_document_lookup_file_finish: - * @self: a #SysprofDocument - * @result: the #GAsyncResult provided to callback - * @error: a location for a #GError, or %NULL - * - * Completes a request to load the contents of a file that was - * embedded within the document. - * - * Returns: (transfer full): a #GBytes if successful; otherwise %NULL - * and @error is set. - */ -GBytes * -sysprof_document_lookup_file_finish (SysprofDocument *self, - GAsyncResult *result, - GError **error) -{ - g_return_val_if_fail (SYSPROF_IS_DOCUMENT (self), NULL); - g_return_val_if_fail (G_IS_TASK (result), NULL); - g_return_val_if_fail (g_task_is_valid (result, self), NULL); - - return g_task_propagate_pointer (G_TASK (result), error); + return NULL; } /** @@ -570,7 +503,7 @@ sysprof_document_list_files (SysprofDocument *self) { g_autoptr(SysprofDocumentFileChunk) file_chunk = sysprof_document_get_item ((GListModel *)self, i); - if (g_strcmp0 (path, sysprof_document_file_chunk_get_path (file_chunk)) != 0) + if (g_strcmp0 (path, sysprof_document_file_chunk_get_path (file_chunk)) == 0) { gboolean is_last = sysprof_document_file_chunk_get_is_last (file_chunk); diff --git a/src/libsysprof-analyze/sysprof-document.h b/src/libsysprof-analyze/sysprof-document.h index efe09c37..a239ca63 100644 --- a/src/libsysprof-analyze/sysprof-document.h +++ b/src/libsysprof-analyze/sysprof-document.h @@ -24,6 +24,7 @@ #include +#include "sysprof-document-file.h" #include "sysprof-document-symbols.h" #include "sysprof-symbolizer.h" @@ -51,18 +52,11 @@ SysprofDocumentSymbols *sysprof_document_symbolize_finish (SysprofDocument GAsyncResult *result, GError **error); SYSPROF_AVAILABLE_IN_ALL +SysprofDocumentFile *sysprof_document_lookup_file (SysprofDocument *self, + const char *path); +SYSPROF_AVAILABLE_IN_ALL GListModel *sysprof_document_list_files (SysprofDocument *self); SYSPROF_AVAILABLE_IN_ALL -void sysprof_document_lookup_file_async (SysprofDocument *self, - const char *filename, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -SYSPROF_AVAILABLE_IN_ALL -GBytes *sysprof_document_lookup_file_finish (SysprofDocument *self, - GAsyncResult *result, - GError **error); -SYSPROF_AVAILABLE_IN_ALL GListModel *sysprof_document_list_traceables (SysprofDocument *self); G_END_DECLS From d8d787c871a9cf3deb965285826b8635c1825187 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 9 May 2023 12:49:07 -0700 Subject: [PATCH 0080/1030] libsysprof-analyze: add test to dump embedded file contents --- src/libsysprof-analyze/tests/meson.build | 1 + .../tests/test-print-file.c | 60 +++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 src/libsysprof-analyze/tests/test-print-file.c diff --git a/src/libsysprof-analyze/tests/meson.build b/src/libsysprof-analyze/tests/meson.build index c328c977..7ffd5b01 100644 --- a/src/libsysprof-analyze/tests/meson.build +++ b/src/libsysprof-analyze/tests/meson.build @@ -14,6 +14,7 @@ libsysprof_analyze_testsuite_c_args = [ libsysprof_analyze_testsuite = { 'test-capture-model' : {'skip': true}, 'test-list-files' : {'skip': true}, + 'test-print-file' : {'skip': true}, 'test-symbolize' : {'skip': true}, } diff --git a/src/libsysprof-analyze/tests/test-print-file.c b/src/libsysprof-analyze/tests/test-print-file.c new file mode 100644 index 00000000..ec593088 --- /dev/null +++ b/src/libsysprof-analyze/tests/test-print-file.c @@ -0,0 +1,60 @@ +/* test-print-file.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include + +int +main (int argc, + char *argv[]) +{ + g_autoptr(SysprofDocumentFile) file = NULL; + g_autoptr(SysprofDocument) document = NULL; + g_autoptr(GListModel) files = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GBytes) bytes = NULL; + + if (argc < 3) + { + g_printerr ("usage: %s CAPTURE_FILE EMBEDDED_FILE_PATH\n", argv[0]); + return 1; + } + + if (!(document = sysprof_document_new (argv[1], &error))) + { + g_printerr ("Failed to open capture: %s\n", error->message); + return 1; + } + + file = sysprof_document_lookup_file (document, argv[2]); + + if (file == NULL) + { + g_printerr ("File \"%s\" not found.\n", argv[2]); + return 1; + } + + bytes = sysprof_document_file_dup_bytes (file); + + write (STDOUT_FILENO, + g_bytes_get_data (bytes, NULL), + g_bytes_get_size (bytes)); + + return 0; +} From b625fec4545fc2d7eb9682b06733141b4171ee77 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 9 May 2023 13:29:52 -0700 Subject: [PATCH 0081/1030] libsysprof-analyze: add basic symbolize API This isn't what it will look like in final form, just get the minimum there for us to use it with the bundled decoder (copied from libsysprof with adaptations). --- .../sysprof-bundled-symbolizer.c | 138 +++++++++++++++++- .../sysprof-document-symbols.c | 12 ++ .../sysprof-symbolizer-private.h | 41 ++++-- src/libsysprof-analyze/sysprof-symbolizer.c | 9 ++ 4 files changed, 183 insertions(+), 17 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-bundled-symbolizer.c b/src/libsysprof-analyze/sysprof-bundled-symbolizer.c index cbe8e239..997c8b49 100644 --- a/src/libsysprof-analyze/sysprof-bundled-symbolizer.c +++ b/src/libsysprof-analyze/sysprof-bundled-symbolizer.c @@ -23,10 +23,30 @@ #include "sysprof-bundled-symbolizer.h" #include "sysprof-document-private.h" #include "sysprof-symbolizer-private.h" +#include "sysprof-symbol-private.h" + +SYSPROF_ALIGNED_BEGIN(1) +typedef struct +{ + SysprofCaptureAddress addr_begin; + SysprofCaptureAddress addr_end; + guint32 pid; + guint32 offset; + guint32 tag_offset; + guint32 padding; +} Decoded +SYSPROF_ALIGNED_END(1); struct _SysprofBundledSymbolizer { - SysprofSymbolizer parent_instance; + SysprofSymbolizer parent_instance; + + const Decoded *symbols; + guint n_symbols; + + GBytes *bytes; + const gchar *beginptr; + const gchar *endptr; }; struct _SysprofBundledSymbolizerClass @@ -41,9 +61,45 @@ sysprof_bundled_symbolizer_decode (SysprofBundledSymbolizer *self, GBytes *bytes, gboolean is_native) { + char *beginptr; + char *endptr; + g_assert (SYSPROF_IS_BUNDLED_SYMBOLIZER (self)); g_assert (bytes != NULL); + /* Our GBytes always contain a trialing \0 after what we think + * is the end of the buffer. + */ + beginptr = (char *)g_bytes_get_data (bytes, NULL); + endptr = beginptr + g_bytes_get_size (bytes); + + for (gchar *ptr = beginptr; + ptr < endptr && (ptr + sizeof (Decoded)) < endptr; + ptr += sizeof (Decoded)) + { + Decoded *sym = (Decoded *)ptr; + + if (sym->addr_begin == 0 && + sym->addr_end == 0 && + sym->pid == 0 && + sym->offset == 0) + { + self->symbols = (const Decoded *)beginptr; + self->n_symbols = sym - self->symbols; + break; + } + else if (!is_native) + { + sym->addr_begin = GUINT64_SWAP_LE_BE (sym->addr_begin); + sym->addr_end = GUINT64_SWAP_LE_BE (sym->addr_end); + sym->pid = GUINT32_SWAP_LE_BE (sym->pid); + sym->offset = GUINT32_SWAP_LE_BE (sym->offset); + sym->tag_offset = GUINT32_SWAP_LE_BE (sym->tag_offset); + } + } + + self->beginptr = beginptr; + self->endptr = endptr; } static void @@ -84,9 +140,88 @@ sysprof_bundled_symbolizer_prepare_finish (SysprofSymbolizer *symbolizer, return g_task_propagate_boolean (G_TASK (result), error); } +static gint +search_for_symbol_cb (gconstpointer a, + gconstpointer b) +{ + const Decoded *key = a; + const Decoded *ele = b; + + if (key->pid < ele->pid) + return -1; + + if (key->pid > ele->pid) + return 1; + + g_assert (key->pid == ele->pid); + + if (key->addr_begin < ele->addr_begin) + return -1; + + if (key->addr_begin > ele->addr_end) + return 1; + + g_assert (key->addr_begin >= ele->addr_begin); + g_assert (key->addr_end <= ele->addr_end); + + return 0; +} + +static SysprofSymbol * +sysprof_bundled_symbolizer_symbolize (SysprofSymbolizer *symbolizer, + gint64 time, + int pid, + SysprofAddress address) +{ + SysprofBundledSymbolizer *self = SYSPROF_BUNDLED_SYMBOLIZER (symbolizer); + g_autoptr(GRefString) tag = NULL; + const Decoded *ret; + const Decoded key = { + .addr_begin = address, + .addr_end = address, + .pid = pid, + .offset = 0, + .tag_offset = 0, + }; + + if (self->n_symbols == 0) + return NULL; + + ret = bsearch (&key, + self->symbols, + self->n_symbols, + sizeof *ret, + search_for_symbol_cb); + + if (ret == NULL || ret->offset == 0) + return NULL; + + if (ret->tag_offset > 0) + { + if (ret->tag_offset < (self->endptr - self->beginptr)) + tag = g_ref_string_new (&self->beginptr[ret->tag_offset]); + } + + if (ret->offset < (self->endptr - self->beginptr)) + return _sysprof_symbol_new (g_ref_string_new (&self->beginptr[ret->offset]), + g_steal_pointer (&tag), + NULL); + + return NULL; +} + static void sysprof_bundled_symbolizer_finalize (GObject *object) { + SysprofBundledSymbolizer *self = (SysprofBundledSymbolizer *)object; + + self->symbols = NULL; + self->n_symbols = 0; + self->beginptr = NULL; + self->endptr = NULL; + + g_clear_pointer (&self->bytes, g_bytes_unref); + G_OBJECT_CLASS (sysprof_bundled_symbolizer_parent_class)->finalize (object); } @@ -100,6 +235,7 @@ sysprof_bundled_symbolizer_class_init (SysprofBundledSymbolizerClass *klass) symbolizer_class->prepare_async = sysprof_bundled_symbolizer_prepare_async; symbolizer_class->prepare_finish = sysprof_bundled_symbolizer_prepare_finish; + symbolizer_class->symbolize = sysprof_bundled_symbolizer_symbolize; } static void diff --git a/src/libsysprof-analyze/sysprof-document-symbols.c b/src/libsysprof-analyze/sysprof-document-symbols.c index 16890562..a275d2ac 100644 --- a/src/libsysprof-analyze/sysprof-document-symbols.c +++ b/src/libsysprof-analyze/sysprof-document-symbols.c @@ -83,11 +83,16 @@ sysprof_document_symbols_add_traceable (SysprofDocumentSymbols *self, SysprofAddressContext last_context; guint64 *addresses; guint n_addresses; + gint64 time; + int pid; g_assert (SYSPROF_IS_DOCUMENT_SYMBOLS (self)); g_assert (SYSPROF_IS_DOCUMENT_TRACEABLE (traceable)); g_assert (SYSPROF_IS_SYMBOLIZER (symbolizer)); + time = sysprof_document_frame_get_time (SYSPROF_DOCUMENT_FRAME (traceable)); + pid = sysprof_document_frame_get_pid (SYSPROF_DOCUMENT_FRAME (traceable)); + /* TODO: We need to get the SysprofMountNamespace for the PID which must have * already been compiled. We also need the list SysprofDocumentMmap so that we * can get the build-id or inode to do various validation checks. @@ -119,6 +124,13 @@ sysprof_document_symbols_add_traceable (SysprofDocumentSymbols *self, } else { + g_autoptr(SysprofSymbol) symbol = _sysprof_symbolizer_symbolize (symbolizer, time, pid, address); + + /* TODO: This isn't the API we'll use for symbolizing, it just gets + * some plumbing in place. Additionally, we'll probably cache all these + * values here so that we can skip calling the symbolizer at all for + * subsequent symbols within a given range. + */ } } } diff --git a/src/libsysprof-analyze/sysprof-symbolizer-private.h b/src/libsysprof-analyze/sysprof-symbolizer-private.h index c70a110c..0416fb29 100644 --- a/src/libsysprof-analyze/sysprof-symbolizer-private.h +++ b/src/libsysprof-analyze/sysprof-symbolizer-private.h @@ -21,6 +21,7 @@ #pragma once #include "sysprof-document.h" +#include "sysprof-symbol.h" #include "sysprof-symbolizer.h" G_BEGIN_DECLS @@ -36,24 +37,32 @@ struct _SysprofSymbolizerClass { GObjectClass parent_class; - void (*prepare_async) (SysprofSymbolizer *self, - SysprofDocument *document, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); - gboolean (*prepare_finish) (SysprofSymbolizer *self, - GAsyncResult *result, - GError **error); + void (*prepare_async) (SysprofSymbolizer *self, + SysprofDocument *document, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + gboolean (*prepare_finish) (SysprofSymbolizer *self, + GAsyncResult *result, + GError **error); + SysprofSymbol *(*symbolize) (SysprofSymbolizer *self, + gint64 time, + int pid, + SysprofAddress address); }; -void _sysprof_symbolizer_prepare_async (SysprofSymbolizer *self, - SysprofDocument *document, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean _sysprof_symbolizer_prepare_finish (SysprofSymbolizer *self, - GAsyncResult *result, - GError **error); +void _sysprof_symbolizer_prepare_async (SysprofSymbolizer *self, + SysprofDocument *document, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean _sysprof_symbolizer_prepare_finish (SysprofSymbolizer *self, + GAsyncResult *result, + GError **error); +SysprofSymbol *_sysprof_symbolizer_symbolize (SysprofSymbolizer *self, + gint64 time, + int pid, + SysprofAddress address); G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-symbolizer.c b/src/libsysprof-analyze/sysprof-symbolizer.c index 8f494903..904d08ae 100644 --- a/src/libsysprof-analyze/sysprof-symbolizer.c +++ b/src/libsysprof-analyze/sysprof-symbolizer.c @@ -89,3 +89,12 @@ _sysprof_symbolizer_prepare_finish (SysprofSymbolizer *self, return SYSPROF_SYMBOLIZER_GET_CLASS (self)->prepare_finish (self, result, error); } + +SysprofSymbol * +_sysprof_symbolizer_symbolize (SysprofSymbolizer *self, + gint64 time, + int pid, + SysprofAddress address) +{ + return SYSPROF_SYMBOLIZER_GET_CLASS (self)->symbolize (self, time, pid, address); +} From e83011c85d800549acd7ba17bee6e45ff659148c Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 9 May 2023 13:30:02 -0700 Subject: [PATCH 0082/1030] libysysprof-analyze: make ref string transfer full No reason to have extra unref's by the caller for these. --- src/libsysprof-analyze/sysprof-symbol.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-symbol.c b/src/libsysprof-analyze/sysprof-symbol.c index 66c03c12..8aef2561 100644 --- a/src/libsysprof-analyze/sysprof-symbol.c +++ b/src/libsysprof-analyze/sysprof-symbol.c @@ -140,16 +140,16 @@ sysprof_symbol_get_binary_path (SysprofSymbol *self) } SysprofSymbol * -_sysprof_symbol_new (char *name, - char *binary_path, - char *binary_nick) +_sysprof_symbol_new (GRefString *name, + GRefString *binary_path, + GRefString *binary_nick) { SysprofSymbol *self; self = g_object_new (SYSPROF_TYPE_SYMBOL, NULL); - self->name = name ? g_ref_string_acquire (name) : NULL; - self->binary_path = binary_path ? g_ref_string_acquire (binary_path) : NULL; - self->binary_nick = binary_nick ? g_ref_string_acquire (binary_nick) : NULL; + self->name = name; + self->binary_path = binary_path; + self->binary_nick = binary_nick; return self; } From 30a30524feb53b6eaed2174bd05f0b144dc8bae1 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 9 May 2023 13:34:35 -0700 Subject: [PATCH 0083/1030] libsysprof-analyze: implement multi-symbolizer symbolize --- .../sysprof-multi-symbolizer.c | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-multi-symbolizer.c b/src/libsysprof-analyze/sysprof-multi-symbolizer.c index f65846ad..ffcc1475 100644 --- a/src/libsysprof-analyze/sysprof-multi-symbolizer.c +++ b/src/libsysprof-analyze/sysprof-multi-symbolizer.c @@ -117,6 +117,26 @@ sysprof_multi_symbolizer_prepare_finish (SysprofSymbolizer *symbolizer, return g_task_propagate_boolean (G_TASK (result), error); } +static SysprofSymbol * +sysprof_multi_symbolizer_symbolize (SysprofSymbolizer *symbolizer, + gint64 time, + int pid, + SysprofAddress address) +{ + SysprofMultiSymbolizer *self = SYSPROF_MULTI_SYMBOLIZER (symbolizer); + + for (guint i = 0; i < self->symbolizers->len; i++) + { + SysprofSymbolizer *child = g_ptr_array_index (self->symbolizers, i); + SysprofSymbol *symbol = _sysprof_symbolizer_symbolize (child, time, pid, address); + + if (symbol != NULL) + return symbol; + } + + return NULL; +} + static void sysprof_multi_symbolizer_finalize (GObject *object) { @@ -137,6 +157,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; } static void From dd710329e56928f367b9d67d13466b92463339f3 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 9 May 2023 13:39:21 -0700 Subject: [PATCH 0084/1030] libsysprof-analyze: keey gbytes around for decoded access --- src/libsysprof-analyze/sysprof-bundled-symbolizer.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-bundled-symbolizer.c b/src/libsysprof-analyze/sysprof-bundled-symbolizer.c index 997c8b49..2f88b6e6 100644 --- a/src/libsysprof-analyze/sysprof-bundled-symbolizer.c +++ b/src/libsysprof-analyze/sysprof-bundled-symbolizer.c @@ -73,7 +73,7 @@ sysprof_bundled_symbolizer_decode (SysprofBundledSymbolizer *self, beginptr = (char *)g_bytes_get_data (bytes, NULL); endptr = beginptr + g_bytes_get_size (bytes); - for (gchar *ptr = beginptr; + for (char *ptr = beginptr; ptr < endptr && (ptr + sizeof (Decoded)) < endptr; ptr += sizeof (Decoded)) { @@ -100,6 +100,7 @@ sysprof_bundled_symbolizer_decode (SysprofBundledSymbolizer *self, self->beginptr = beginptr; self->endptr = endptr; + self->bytes = g_bytes_ref (bytes); } static void @@ -187,10 +188,13 @@ sysprof_bundled_symbolizer_symbolize (SysprofSymbolizer *symbolizer, if (self->n_symbols == 0) return NULL; + g_assert (self->symbols != NULL); + g_assert (self->n_symbols > 0); + ret = bsearch (&key, self->symbols, self->n_symbols, - sizeof *ret, + sizeof (Decoded), search_for_symbol_cb); if (ret == NULL || ret->offset == 0) From 6f0f8c980a5d599fc4f3980749b1b497986cec03 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 9 May 2023 15:44:09 -0700 Subject: [PATCH 0085/1030] libsysprof-analyze: mark refstring in prototypes --- src/libsysprof-analyze/sysprof-symbol-private.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-symbol-private.h b/src/libsysprof-analyze/sysprof-symbol-private.h index acbc26ec..8a7146d1 100644 --- a/src/libsysprof-analyze/sysprof-symbol-private.h +++ b/src/libsysprof-analyze/sysprof-symbol-private.h @@ -25,8 +25,8 @@ G_BEGIN_DECLS -SysprofSymbol *_sysprof_symbol_new (char *name, - char *binary_nick, - char *binary_path); +SysprofSymbol *_sysprof_symbol_new (GRefString *name, + GRefString *binary_nick, + GRefString *binary_path); G_END_DECLS From 100337232423a922566a338b6f660b2256629e19 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 9 May 2023 15:57:29 -0700 Subject: [PATCH 0086/1030] libsysprof-analyze: add listmodel of processes This is handy to be able to show a list of processes that were contained within a particular capture file. --- src/libsysprof-analyze/sysprof-document.c | 28 ++++++++- src/libsysprof-analyze/sysprof-document.h | 2 + src/libsysprof-analyze/tests/meson.build | 9 +-- .../tests/test-list-processes.c | 57 +++++++++++++++++++ 4 files changed, 90 insertions(+), 6 deletions(-) create mode 100644 src/libsysprof-analyze/tests/test-list-processes.c diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index 5771daa9..ef9ebd51 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -43,6 +43,7 @@ struct _SysprofDocument GtkBitset *file_chunks; GtkBitset *traceables; + GtkBitset *processes; GHashTable *files_first_position; @@ -117,6 +118,7 @@ sysprof_document_finalize (GObject *object) g_clear_pointer (&self->frames, g_array_unref); g_clear_pointer (&self->strings, g_hash_table_unref); g_clear_pointer (&self->traceables, gtk_bitset_unref); + g_clear_pointer (&self->processes, gtk_bitset_unref); g_clear_pointer (&self->file_chunks, gtk_bitset_unref); g_clear_pointer (&self->files_first_position, g_hash_table_unref); @@ -142,6 +144,7 @@ sysprof_document_init (SysprofDocument *self) (GDestroyNotify)g_ref_string_release); self->traceables = gtk_bitset_new_empty (); self->file_chunks = gtk_bitset_new_empty (); + self->processes = gtk_bitset_new_empty (); self->files_first_position = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); } @@ -212,15 +215,19 @@ sysprof_document_load (SysprofDocument *self, ptr.length = frame_len; tainted = (const SysprofCaptureFrame *)(gpointer)&self->base[pos]; + if (tainted->type == SYSPROF_CAPTURE_FRAME_SAMPLE || tainted->type == SYSPROF_CAPTURE_FRAME_ALLOCATION) gtk_bitset_add (self->traceables, self->frames->len); + else if (tainted->type == SYSPROF_CAPTURE_FRAME_PROCESS) + gtk_bitset_add (self->processes, self->frames->len); else if (tainted->type == SYSPROF_CAPTURE_FRAME_FILE_CHUNK) + gtk_bitset_add (self->file_chunks, self->frames->len); + + if (tainted->type == SYSPROF_CAPTURE_FRAME_FILE_CHUNK) { const SysprofCaptureFileChunk *file_chunk = (const SysprofCaptureFileChunk *)tainted; - gtk_bitset_add (self->file_chunks, self->frames->len); - if (has_null_byte (file_chunk->path, (const char *)file_chunk->data) && !g_hash_table_contains (self->files_first_position, file_chunk->path)) g_hash_table_insert (self->files_first_position, @@ -540,3 +547,20 @@ sysprof_document_list_traceables (SysprofDocument *self) return _sysprof_document_bitset_index_new (G_LIST_MODEL (self), self->traceables); } + +/** + * sysprof_document_list_processes: + * @self: a #SysprofDocument + * + * Gets a #GListModel containing #SysprofDocumentProcess found within + * the #SysprofDocument. + * + * Returns: (transfer full): a #GListModel + */ +GListModel * +sysprof_document_list_processes (SysprofDocument *self) +{ + g_return_val_if_fail (SYSPROF_IS_DOCUMENT (self), NULL); + + return _sysprof_document_bitset_index_new (G_LIST_MODEL (self), self->processes); +} diff --git a/src/libsysprof-analyze/sysprof-document.h b/src/libsysprof-analyze/sysprof-document.h index a239ca63..0fc3d08e 100644 --- a/src/libsysprof-analyze/sysprof-document.h +++ b/src/libsysprof-analyze/sysprof-document.h @@ -58,5 +58,7 @@ SYSPROF_AVAILABLE_IN_ALL GListModel *sysprof_document_list_files (SysprofDocument *self); SYSPROF_AVAILABLE_IN_ALL GListModel *sysprof_document_list_traceables (SysprofDocument *self); +SYSPROF_AVAILABLE_IN_ALL +GListModel *sysprof_document_list_processes (SysprofDocument *self); G_END_DECLS diff --git a/src/libsysprof-analyze/tests/meson.build b/src/libsysprof-analyze/tests/meson.build index 7ffd5b01..b1a045cc 100644 --- a/src/libsysprof-analyze/tests/meson.build +++ b/src/libsysprof-analyze/tests/meson.build @@ -12,10 +12,11 @@ libsysprof_analyze_testsuite_c_args = [ ] libsysprof_analyze_testsuite = { - 'test-capture-model' : {'skip': true}, - 'test-list-files' : {'skip': true}, - 'test-print-file' : {'skip': true}, - 'test-symbolize' : {'skip': true}, + 'test-capture-model' : {'skip': true}, + 'test-list-files' : {'skip': true}, + 'test-print-file' : {'skip': true}, + 'test-list-processes' : {'skip': true}, + 'test-symbolize' : {'skip': true}, } libsysprof_analyze_testsuite_deps = [ diff --git a/src/libsysprof-analyze/tests/test-list-processes.c b/src/libsysprof-analyze/tests/test-list-processes.c new file mode 100644 index 00000000..4561af33 --- /dev/null +++ b/src/libsysprof-analyze/tests/test-list-processes.c @@ -0,0 +1,57 @@ +/* test-list-processes.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include + +int +main (int argc, + char *argv[]) +{ + g_autoptr(SysprofDocument) document = NULL; + g_autoptr(GListModel) processes = NULL; + g_autoptr(GError) error = NULL; + guint n_items; + + if (argc < 2) + { + g_printerr ("usage: %s CAPTURE_FILE\n", argv[0]); + return 1; + } + + if (!(document = sysprof_document_new (argv[1], &error))) + { + g_printerr ("Failed to open capture: %s\n", error->message); + return 1; + } + + processes = sysprof_document_list_processes (document); + n_items = g_list_model_get_n_items (processes); + + for (guint i = 0; i < n_items; i++) + { + g_autoptr(SysprofDocumentProcess) process = g_list_model_get_item (processes, i); + + g_print ("%d: %s\n", + sysprof_document_frame_get_pid (SYSPROF_DOCUMENT_FRAME (process)), + sysprof_document_process_get_command_line (process)); + } + + return 0; +} From f220484746635d3a3ece0900919d0c45dae85a77 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 9 May 2023 16:57:10 -0700 Subject: [PATCH 0087/1030] libsysprof-analyze: index memory maps by pid We will want to use this when loading processes along with the system mounts so that we can locate files by buildid/inode/etc. --- src/libsysprof-analyze/sysprof-document.c | 82 +++++++++++++++++++++-- 1 file changed, 75 insertions(+), 7 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index ef9ebd51..a41cdca8 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -30,6 +30,7 @@ #include "sysprof-document-file-chunk.h" #include "sysprof-document-file-private.h" #include "sysprof-document-frame-private.h" +#include "sysprof-document-mmap.h" #include "sysprof-document-symbols-private.h" #include "sysprof-symbolizer-private.h" @@ -44,8 +45,10 @@ struct _SysprofDocument GtkBitset *file_chunks; GtkBitset *traceables; GtkBitset *processes; + GtkBitset *mmaps; GHashTable *files_first_position; + GHashTable *pid_to_mmaps; GMutex strings_mutex; GHashTable *strings; @@ -109,17 +112,34 @@ _sysprof_document_traceables (SysprofDocument *self) return self->traceables; } +static inline gboolean +has_null_byte (const char *str, + const char *endptr) +{ + for (const char *c = str; c < endptr; c++) + { + if (*c == '\0') + return TRUE; + } + + return FALSE; +} + static void sysprof_document_finalize (GObject *object) { SysprofDocument *self = (SysprofDocument *)object; + g_clear_pointer (&self->pid_to_mmaps, g_hash_table_unref); g_clear_pointer (&self->mapped_file, g_mapped_file_unref); g_clear_pointer (&self->frames, g_array_unref); g_clear_pointer (&self->strings, g_hash_table_unref); + g_clear_pointer (&self->traceables, gtk_bitset_unref); g_clear_pointer (&self->processes, gtk_bitset_unref); + g_clear_pointer (&self->mmaps, gtk_bitset_unref); g_clear_pointer (&self->file_chunks, gtk_bitset_unref); + g_clear_pointer (&self->files_first_position, g_hash_table_unref); g_mutex_clear (&self->strings_mutex); @@ -142,24 +162,68 @@ sysprof_document_init (SysprofDocument *self) self->frames = g_array_new (FALSE, FALSE, sizeof (SysprofDocumentFramePointer)); self->strings = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify)g_ref_string_release); + self->traceables = gtk_bitset_new_empty (); self->file_chunks = gtk_bitset_new_empty (); self->processes = gtk_bitset_new_empty (); + self->mmaps = gtk_bitset_new_empty (); self->files_first_position = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + self->pid_to_mmaps = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_ptr_array_unref); } -static inline gboolean -has_null_byte (const char *str, - const char *endptr) +static int +compare_map (gconstpointer a, + gconstpointer b) { - for (const char *c = str; c < endptr; c++) + SysprofDocumentMmap * const *mmap_a = a; + SysprofDocumentMmap * const *mmap_b = b; + SysprofAddress addr_a = sysprof_document_mmap_get_start_address (*mmap_a); + SysprofAddress addr_b = sysprof_document_mmap_get_start_address (*mmap_b); + + if (addr_a < addr_b) + return -1; + else if (addr_a > addr_b) + return 1; + else + return 0; +} + +static void +sysprof_document_load_memory_maps (SysprofDocument *self) +{ + GtkBitsetIter iter; + GHashTableIter hiter; + gpointer key, value; + guint i; + + g_assert (SYSPROF_IS_DOCUMENT (self)); + + if (gtk_bitset_iter_init_first (&iter, self->mmaps, &i)) { - if (*c == '\0') - return TRUE; + do + { + g_autoptr(SysprofDocumentMmap) map = sysprof_document_get_item ((GListModel *)self, i); + int pid = sysprof_document_frame_get_pid (SYSPROF_DOCUMENT_FRAME (map)); + GPtrArray *maps = g_hash_table_lookup (self->pid_to_mmaps, GINT_TO_POINTER (pid)); + + if G_UNLIKELY (maps == NULL) + { + maps = g_ptr_array_new_with_free_func (g_object_unref); + g_hash_table_insert (self->pid_to_mmaps, GINT_TO_POINTER (pid), maps); + } + + g_ptr_array_add (maps, g_steal_pointer (&map)); + } + while (gtk_bitset_iter_next (&iter, &i)); } - return FALSE; + g_hash_table_iter_init (&hiter, self->pid_to_mmaps); + while (g_hash_table_iter_next (&hiter, &key, &value)) + { + GPtrArray *ar = value; + g_ptr_array_sort (ar, compare_map); + } } static gboolean @@ -223,6 +287,8 @@ sysprof_document_load (SysprofDocument *self, gtk_bitset_add (self->processes, self->frames->len); else if (tainted->type == SYSPROF_CAPTURE_FRAME_FILE_CHUNK) gtk_bitset_add (self->file_chunks, self->frames->len); + else if (tainted->type == SYSPROF_CAPTURE_FRAME_MAP) + gtk_bitset_add (self->mmaps, self->frames->len); if (tainted->type == SYSPROF_CAPTURE_FRAME_FILE_CHUNK) { @@ -240,6 +306,8 @@ sysprof_document_load (SysprofDocument *self, g_array_append_val (self->frames, ptr); } + sysprof_document_load_memory_maps (self); + return TRUE; } From a22eb8c435194e1c0629a0c10603384fee5910b5 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 9 May 2023 17:18:44 -0700 Subject: [PATCH 0088/1030] libsysprof-analyze: add inlined line reader This is a bit nicer than the allocating form we have elsewhere. --- src/libsysprof-analyze/line-reader-private.h | 94 ++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 src/libsysprof-analyze/line-reader-private.h diff --git a/src/libsysprof-analyze/line-reader-private.h b/src/libsysprof-analyze/line-reader-private.h new file mode 100644 index 00000000..deea4396 --- /dev/null +++ b/src/libsysprof-analyze/line-reader-private.h @@ -0,0 +1,94 @@ +/* line-reader-private.h + * + * Copyright 2015-2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include +#include + +G_BEGIN_DECLS + +typedef struct _LineReader +{ + char *contents; + gsize length; + gssize pos; +} LineReader; + +static inline void +line_reader_init (LineReader *reader, + char *contents, + gssize length) +{ + g_assert (reader != NULL); + + if (length < 0) + length = strlen (contents); + + if (contents != NULL) + { + reader->contents = contents; + reader->length = length; + reader->pos = 0; + } + else + { + reader->contents = NULL; + reader->length = 0; + reader->pos = 0; + } +} + +static inline char * +line_reader_next (LineReader *reader, + gsize *length) +{ + char *ret = NULL; + + g_assert (reader != NULL); + g_assert (length != NULL); + + if ((reader->contents == NULL) || (reader->pos >= reader->length)) + { + *length = 0; + return NULL; + } + + ret = &reader->contents [reader->pos]; + + for (; reader->pos < reader->length; reader->pos++) + { + if (reader->contents [reader->pos] == '\n') + { + *length = &reader->contents [reader->pos] - ret; + /* Ingore the \r in \r\n if provided */ + if (*length > 0 && reader->pos > 0 && reader->contents [reader->pos - 1] == '\r') + (*length)--; + reader->pos++; + return ret; + } + } + + *length = &reader->contents [reader->pos] - ret; + + return ret; +} + +G_END_DECLS From f01346da9028552b34b80083cbf64d3f3f374e77 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 9 May 2023 17:19:23 -0700 Subject: [PATCH 0089/1030] libsysprof-analyze: add missing constructor --- src/libsysprof-analyze/sysprof-mount-device.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-mount-device.c b/src/libsysprof-analyze/sysprof-mount-device.c index e93002b1..3b61fc3b 100644 --- a/src/libsysprof-analyze/sysprof-mount-device.c +++ b/src/libsysprof-analyze/sysprof-mount-device.c @@ -140,6 +140,12 @@ sysprof_mount_device_init (SysprofMountDevice *self) { } +SysprofMountDevice * +sysprof_mount_device_new (void) +{ + return g_object_new (SYSPROF_TYPE_MOUNT_DEVICE, NULL); +} + const char * sysprof_mount_device_get_id (SysprofMountDevice *self) { From 6290a1736d95560b27f0b250c947898db667e51f Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 9 May 2023 17:20:00 -0700 Subject: [PATCH 0090/1030] libsysprof-analyze: parse core mount namespace devices We want to track the core mount namespace, for which the namespace we use for processes will derive (to be done). --- src/libsysprof-analyze/sysprof-document.c | 99 +++++++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index a41cdca8..e1500955 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -32,8 +32,12 @@ #include "sysprof-document-frame-private.h" #include "sysprof-document-mmap.h" #include "sysprof-document-symbols-private.h" +#include "sysprof-mount-device-private.h" +#include "sysprof-mount-namespace-private.h" #include "sysprof-symbolizer-private.h" +#include "line-reader-private.h" + struct _SysprofDocument { GObject parent_instance; @@ -50,6 +54,8 @@ struct _SysprofDocument GHashTable *files_first_position; GHashTable *pid_to_mmaps; + SysprofMountNamespace *mount_namespace; + GMutex strings_mutex; GHashTable *strings; @@ -112,6 +118,18 @@ _sysprof_document_traceables (SysprofDocument *self) return self->traceables; } +static void +decode_space (gchar **str) +{ + /* Replace encoded space "\040" with ' ' */ + if (strstr (*str, "\\040")) + { + g_auto(GStrv) parts = g_strsplit (*str, "\\040", 0); + g_free (*str); + *str = g_strjoinv (" ", parts); + } +} + static inline gboolean has_null_byte (const char *str, const char *endptr) @@ -140,6 +158,8 @@ sysprof_document_finalize (GObject *object) g_clear_pointer (&self->mmaps, gtk_bitset_unref); g_clear_pointer (&self->file_chunks, gtk_bitset_unref); + g_clear_object (&self->mount_namespace); + g_clear_pointer (&self->files_first_position, g_hash_table_unref); g_mutex_clear (&self->strings_mutex); @@ -170,6 +190,8 @@ sysprof_document_init (SysprofDocument *self) self->files_first_position = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); self->pid_to_mmaps = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_ptr_array_unref); + + self->mount_namespace = sysprof_mount_namespace_new (); } static int @@ -226,6 +248,82 @@ sysprof_document_load_memory_maps (SysprofDocument *self) } } +static void +sysprof_document_load_mounts (SysprofDocument *self) +{ + g_autoptr(SysprofDocumentFile) file = NULL; + g_autoptr(GBytes) bytes = NULL; + LineReader reader; + const char *contents; + gsize contents_len; + gsize line_len; + char *line; + + g_assert (SYSPROF_IS_DOCUMENT (self)); + + if (!(file = sysprof_document_lookup_file (self, "/proc/mounts")) || + !(bytes = sysprof_document_file_dup_bytes (file))) + return; + + contents = g_bytes_get_data (bytes, &contents_len); + + g_assert (contents != NULL); + g_assert (contents[contents_len] == 0); + + line_reader_init (&reader, (char *)contents, contents_len); + while ((line = line_reader_next (&reader, &line_len))) + { + g_autoptr(SysprofMountDevice) mount_device = NULL; + g_auto(GStrv) parts = NULL; + g_autofree char *subvol = NULL; + const char *filesystem; + const char *mountpoint; + const char *device; + const char *options; + + line[line_len] = 0; + + parts = g_strsplit (line, " ", 5); + + /* Field 0: device + * Field 1: mountpoint + * Field 2: filesystem + * Field 3: Options + * .. Ignored .. + */ + if (g_strv_length (parts) != 5) + continue; + + for (guint j = 0; parts[j]; j++) + decode_space (&parts[j]); + + device = parts[0]; + mountpoint = parts[1]; + filesystem = parts[2]; + options = parts[3]; + + if (g_strcmp0 (filesystem, "btrfs") == 0) + { + g_auto(GStrv) opts = g_strsplit (options, ",", 0); + + for (guint k = 0; opts[k]; k++) + { + if (g_str_has_prefix (opts[k], "subvol=")) + { + subvol = g_strdup (opts[k] + strlen ("subvol=")); + break; + } + } + } + + mount_device = sysprof_mount_device_new (); + sysprof_mount_device_set_id (mount_device, device); + sysprof_mount_device_set_mount_path (mount_device, mountpoint); + sysprof_mount_device_set_subvolume (mount_device, subvol); + sysprof_mount_namespace_add_device (self->mount_namespace, g_steal_pointer (&mount_device)); + } +} + static gboolean sysprof_document_load (SysprofDocument *self, int capture_fd, @@ -306,6 +404,7 @@ sysprof_document_load (SysprofDocument *self, g_array_append_val (self->frames, ptr); } + sysprof_document_load_mounts (self); sysprof_document_load_memory_maps (self); return TRUE; From a9f615cff0ecfbea724afd22b98eafa07ea24519 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 9 May 2023 17:51:38 -0700 Subject: [PATCH 0091/1030] libsysprof-analyze: add scaffolding for mountinfo parsing I want to do this differently than we did in libsysprof, which is going to require a bit of thinking on how we should represent something like a SysprofMount within the mount namespace. --- src/libsysprof-analyze/sysprof-document.c | 74 +++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index e1500955..a5ae29e3 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -53,6 +53,7 @@ struct _SysprofDocument GHashTable *files_first_position; GHashTable *pid_to_mmaps; + GHashTable *pid_to_mountinfo; SysprofMountNamespace *mount_namespace; @@ -149,6 +150,7 @@ sysprof_document_finalize (GObject *object) SysprofDocument *self = (SysprofDocument *)object; g_clear_pointer (&self->pid_to_mmaps, g_hash_table_unref); + g_clear_pointer (&self->pid_to_mountinfo, g_hash_table_unref); g_clear_pointer (&self->mapped_file, g_mapped_file_unref); g_clear_pointer (&self->frames, g_array_unref); g_clear_pointer (&self->strings, g_hash_table_unref); @@ -190,6 +192,7 @@ sysprof_document_init (SysprofDocument *self) self->files_first_position = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); self->pid_to_mmaps = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_ptr_array_unref); + self->pid_to_mountinfo = g_hash_table_new_full (NULL, NULL, NULL, g_object_unref); self->mount_namespace = sysprof_mount_namespace_new (); } @@ -324,6 +327,76 @@ sysprof_document_load_mounts (SysprofDocument *self) } } +static void +sysprof_document_load_mountinfo_line (SysprofDocument *self, + int pid, + const char *line) +{ + g_auto(GStrv) parts = NULL; + gsize n_parts; + + g_assert (SYSPROF_IS_DOCUMENT (self)); + g_assert (line != NULL); + + parts = g_strsplit (line, " ", 0); + n_parts = g_strv_length (parts); + + if (n_parts < 10) + return; + +} + +static void +sysprof_document_load_mountinfo (SysprofDocument *self, + int pid, + GBytes *bytes) +{ + const char *contents; + LineReader reader; + gsize contents_len; + gsize line_len; + char *line; + + g_assert (SYSPROF_IS_DOCUMENT (self)); + g_assert (bytes != NULL); + + contents = g_bytes_get_data (bytes, &contents_len); + + g_assert (contents != NULL); + g_assert (contents[contents_len] == 0); + + line_reader_init (&reader, (char *)contents, contents_len); + while ((line = line_reader_next (&reader, &line_len))) + { + line[line_len] = 0; + sysprof_document_load_mountinfo_line (self, pid, line); + } +} + +static void +sysprof_document_load_mountinfos (SysprofDocument *self) +{ + GHashTableIter hiter; + gpointer key, value; + + g_assert (SYSPROF_IS_DOCUMENT (self)); + + g_hash_table_iter_init (&hiter, self->pid_to_mmaps); + while (g_hash_table_iter_next (&hiter, &key, &value)) + { + g_autoptr(SysprofMountNamespace) mount_namespace = sysprof_mount_namespace_new (); + int pid = GPOINTER_TO_INT (key); + g_autofree char *path = g_strdup_printf ("/proc/%d/mountinfo", pid); + g_autoptr(SysprofDocumentFile) file = sysprof_document_lookup_file (self, path); + + if (file != NULL) + { + g_autoptr(GBytes) bytes = sysprof_document_file_dup_bytes (file); + sysprof_document_load_mountinfo (self, pid, bytes); + } + } +} + static gboolean sysprof_document_load (SysprofDocument *self, int capture_fd, @@ -405,6 +478,7 @@ sysprof_document_load (SysprofDocument *self, } sysprof_document_load_mounts (self); + sysprof_document_load_mountinfos (self); sysprof_document_load_memory_maps (self); return TRUE; From fd6256e68ff5db4628f3d169942a00ff2e9bd12a Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 9 May 2023 20:46:10 -0700 Subject: [PATCH 0092/1030] libsysprof-analyze: start on object to represent a mount --- src/libsysprof-analyze/meson.build | 1 + .../sysprof-mount-private.h | 41 ++++ src/libsysprof-analyze/sysprof-mount.c | 198 ++++++++++++++++++ 3 files changed, 240 insertions(+) create mode 100644 src/libsysprof-analyze/sysprof-mount-private.h create mode 100644 src/libsysprof-analyze/sysprof-mount.c diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build index 0939bcf2..905fd5b6 100644 --- a/src/libsysprof-analyze/meson.build +++ b/src/libsysprof-analyze/meson.build @@ -25,6 +25,7 @@ libsysprof_analyze_private_sources = [ 'sysprof-document-bitset-index.c', 'sysprof-mapped-file.c', 'sysprof-memory-map.c', + 'sysprof-mount.c', 'sysprof-mount-device.c', 'sysprof-mount-namespace.c', ] diff --git a/src/libsysprof-analyze/sysprof-mount-private.h b/src/libsysprof-analyze/sysprof-mount-private.h new file mode 100644 index 00000000..081fef17 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-mount-private.h @@ -0,0 +1,41 @@ +/* sysprof-mount-private.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_MOUNT (sysprof_mount_get_type()) + +G_DECLARE_FINAL_TYPE (SysprofMount, sysprof_mount, SYSPROF, MOUNT, GObject) + +SysprofMount *sysprof_mount_new_for_mountinfo (const char *mountinfo); +int sysprof_mount_get_device_major (SysprofMount *self); +int sysprof_mount_get_device_minor (SysprofMount *self); +const char *sysprof_mount_get_root (SysprofMount *self); +const char *sysprof_mount_get_mount_path (SysprofMount *self); +const char *sysprof_mount_get_mount_source (SysprofMount *self); +const char *sysprof_mount_get_filesystem_type (SysprofMount *self); +const char *sysprof_mount_get_superblock_option (SysprofMount *self, + const char *option); + +G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-mount.c b/src/libsysprof-analyze/sysprof-mount.c new file mode 100644 index 00000000..786bf111 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-mount.c @@ -0,0 +1,198 @@ +/* sysprof-mount.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-mount-private.h" + +struct _SysprofMount +{ + GObject parent_instance; + int device_major; + int device_minor; + GRefString *root; + GRefString *mount_path; + GRefString *mount_source; + GRefString *filesystem_type; + GRefString *superblock_options; +}; + +enum { + PROP_0, + PROP_DEVICE_MAJOR, + PROP_DEVICE_MINOR, + PROP_ROOT, + PROP_MOUNT_PATH, + PROP_MOUNT_SOURCE, + PROP_FILESYSTEM_TYPE, + N_PROPS +}; + +G_DEFINE_FINAL_TYPE (SysprofMount, sysprof_mount, G_TYPE_OBJECT) + +static GParamSpec *properties [N_PROPS]; + +static void +sysprof_mount_finalize (GObject *object) +{ + SysprofMount *self = (SysprofMount *)object; + + g_clear_pointer (&self->root, g_ref_string_release); + g_clear_pointer (&self->mount_path, g_ref_string_release); + g_clear_pointer (&self->mount_source, g_ref_string_release); + g_clear_pointer (&self->filesystem_type, g_ref_string_release); + g_clear_pointer (&self->superblock_options, g_ref_string_release); + + G_OBJECT_CLASS (sysprof_mount_parent_class)->finalize (object); +} + +static void +sysprof_mount_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofMount *self = SYSPROF_MOUNT (object); + + switch (prop_id) + { + case PROP_DEVICE_MAJOR: + g_value_set_int (value, sysprof_mount_get_device_major (self)); + break; + + case PROP_DEVICE_MINOR: + g_value_set_int (value, sysprof_mount_get_device_minor (self)); + break; + + case PROP_ROOT: + g_value_set_string (value, sysprof_mount_get_root (self)); + break; + + case PROP_MOUNT_PATH: + g_value_set_string (value, sysprof_mount_get_mount_path (self)); + break; + + case PROP_MOUNT_SOURCE: + g_value_set_string (value, sysprof_mount_get_mount_source (self)); + break; + + case PROP_FILESYSTEM_TYPE: + g_value_set_string (value, sysprof_mount_get_filesystem_type (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_mount_class_init (SysprofMountClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = sysprof_mount_finalize; + object_class->get_property = sysprof_mount_get_property; + + properties[PROP_DEVICE_MAJOR] = + g_param_spec_int ("device-major", NULL, NULL, + G_MININT, G_MAXINT, 0, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + properties[PROP_DEVICE_MINOR] = + g_param_spec_int ("device-minor", NULL, NULL, + G_MININT, G_MAXINT, 0, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + properties[PROP_ROOT] = + g_param_spec_string ("root", NULL, NULL, + NULL, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + properties[PROP_MOUNT_PATH] = + g_param_spec_string ("mount-path", NULL, NULL, + NULL, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + properties[PROP_MOUNT_SOURCE] = + g_param_spec_string ("mount-source", NULL, NULL, + NULL, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + properties[PROP_FILESYSTEM_TYPE] = + g_param_spec_string ("filesystem-type", NULL, NULL, + NULL, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); +} + +static void +sysprof_mount_init (SysprofMount *self) +{ +} + +SysprofMount * +sysprof_mount_new_for_mountinfo (const char *mountinfo) +{ + return NULL; +} + +int +sysprof_mount_get_device_major (SysprofMount *self) +{ + return self->device_major; +} + +int +sysprof_mount_get_device_minor (SysprofMount *self) +{ + return self->device_minor; +} + +const char * +sysprof_mount_get_root (SysprofMount *self) +{ + return self->root; +} + +const char * +sysprof_mount_get_mount_path (SysprofMount *self) +{ + return self->mount_path; +} + +const char * +sysprof_mount_get_mount_source (SysprofMount *self) +{ + return self->mount_source; +} + +const char * +sysprof_mount_get_filesystem_type (SysprofMount *self) +{ + return self->filesystem_type; +} + +const char * +sysprof_mount_get_superblock_option (SysprofMount *self, + const char *option) +{ + return NULL; +} From fa39a3291a8a4e94f36d8264b46819601bf56589 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 9 May 2023 21:03:13 -0700 Subject: [PATCH 0093/1030] libsysprof-analyze: start plumbing mounts into namespaces --- src/libsysprof-analyze/sysprof-document.c | 29 ++++++------------- .../sysprof-mount-namespace-private.h | 5 ++-- .../sysprof-mount-namespace.c | 18 ++++++++++++ 3 files changed, 29 insertions(+), 23 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index a5ae29e3..60acbd51 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -32,6 +32,7 @@ #include "sysprof-document-frame-private.h" #include "sysprof-document-mmap.h" #include "sysprof-document-symbols-private.h" +#include "sysprof-mount-private.h" #include "sysprof-mount-device-private.h" #include "sysprof-mount-namespace-private.h" #include "sysprof-symbolizer-private.h" @@ -327,30 +328,12 @@ sysprof_document_load_mounts (SysprofDocument *self) } } -static void -sysprof_document_load_mountinfo_line (SysprofDocument *self, - int pid, - const char *line) -{ - g_auto(GStrv) parts = NULL; - gsize n_parts; - - g_assert (SYSPROF_IS_DOCUMENT (self)); - g_assert (line != NULL); - - parts = g_strsplit (line, " ", 0); - n_parts = g_strv_length (parts); - - if (n_parts < 10) - return; - -} - static void sysprof_document_load_mountinfo (SysprofDocument *self, int pid, GBytes *bytes) { + g_autoptr(SysprofMountNamespace) mount_namespace = NULL; const char *contents; LineReader reader; gsize contents_len; @@ -365,11 +348,17 @@ sysprof_document_load_mountinfo (SysprofDocument *self, g_assert (contents != NULL); g_assert (contents[contents_len] == 0); + mount_namespace = sysprof_mount_namespace_copy (self->mount_namespace); + line_reader_init (&reader, (char *)contents, contents_len); while ((line = line_reader_next (&reader, &line_len))) { + g_autoptr(SysprofMount) mount = NULL; + line[line_len] = 0; - sysprof_document_load_mountinfo_line (self, pid, line); + + if ((mount = sysprof_mount_new_for_mountinfo (line))) + sysprof_mount_namespace_add_mount (mount_namespace, g_steal_pointer (&mount)); } } diff --git a/src/libsysprof-analyze/sysprof-mount-namespace-private.h b/src/libsysprof-analyze/sysprof-mount-namespace-private.h index 1a5cea0b..28b110d9 100644 --- a/src/libsysprof-analyze/sysprof-mount-namespace-private.h +++ b/src/libsysprof-analyze/sysprof-mount-namespace-private.h @@ -22,6 +22,7 @@ #include +#include "sysprof-mount-private.h" #include "sysprof-mount-device-private.h" G_BEGIN_DECLS @@ -35,9 +36,7 @@ SysprofMountNamespace *sysprof_mount_namespace_copy (SysprofMountNamespa void sysprof_mount_namespace_add_device (SysprofMountNamespace *self, SysprofMountDevice *mount); void sysprof_mount_namespace_add_mount (SysprofMountNamespace *self, - const char *path, - const char *host_path, - int layer); + SysprofMount *mount); char **sysprof_mount_namespace_translate (SysprofMountNamespace *self, const char *path); GMappedFile *sysprof_mount_namespace_open (SysprofMountNamespace *self, diff --git a/src/libsysprof-analyze/sysprof-mount-namespace.c b/src/libsysprof-analyze/sysprof-mount-namespace.c index c660b5ed..d09e33d6 100644 --- a/src/libsysprof-analyze/sysprof-mount-namespace.c +++ b/src/libsysprof-analyze/sysprof-mount-namespace.c @@ -26,6 +26,7 @@ struct _SysprofMountNamespace { GObject parent_instance; GPtrArray *devices; + GPtrArray *mounts; }; G_DEFINE_FINAL_TYPE (SysprofMountNamespace, sysprof_mount_namespace, G_TYPE_OBJECT) @@ -36,6 +37,7 @@ sysprof_mount_namespace_finalize (GObject *object) SysprofMountNamespace *self = (SysprofMountNamespace *)object; g_clear_pointer (&self->devices, g_ptr_array_unref); + g_clear_pointer (&self->mounts, g_ptr_array_unref); G_OBJECT_CLASS (sysprof_mount_namespace_parent_class)->finalize (object); } @@ -92,3 +94,19 @@ sysprof_mount_namespace_add_device (SysprofMountNamespace *self, g_ptr_array_add (self->devices, device); } + +/** + * sysprof_mount_namespace_add_mount: + * @self: a #SysprofMountNamespace + * @mount: (transfer full): a #SysprofMount + * + */ +void +sysprof_mount_namespace_add_mount (SysprofMountNamespace *self, + SysprofMount *mount) +{ + g_return_if_fail (SYSPROF_IS_MOUNT_NAMESPACE (self)); + g_return_if_fail (SYSPROF_IS_MOUNT (mount)); + + g_ptr_array_add (self->mounts, mount); +} From 7118c38b2bcda649fcdccf5b24fab266f319a906 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 10 May 2023 12:42:28 -0700 Subject: [PATCH 0094/1030] libsysprof-analyze: break out string helper We'll want strings to be deduplicated a bunch, and may need to pass this around to make that a bit easier to ensure. --- src/libsysprof-analyze/meson.build | 1 + src/libsysprof-analyze/sysprof-document.c | 31 ++---- .../sysprof-strings-private.h | 37 +++++++ src/libsysprof-analyze/sysprof-strings.c | 96 +++++++++++++++++++ 4 files changed, 141 insertions(+), 24 deletions(-) create mode 100644 src/libsysprof-analyze/sysprof-strings-private.h create mode 100644 src/libsysprof-analyze/sysprof-strings.c diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build index 905fd5b6..9f150797 100644 --- a/src/libsysprof-analyze/meson.build +++ b/src/libsysprof-analyze/meson.build @@ -28,6 +28,7 @@ libsysprof_analyze_private_sources = [ 'sysprof-mount.c', 'sysprof-mount-device.c', 'sysprof-mount-namespace.c', + 'sysprof-strings.c', ] libsysprof_analyze_public_headers = [ diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index 60acbd51..bc2e6949 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -35,6 +35,7 @@ #include "sysprof-mount-private.h" #include "sysprof-mount-device-private.h" #include "sysprof-mount-namespace-private.h" +#include "sysprof-strings-private.h" #include "sysprof-symbolizer-private.h" #include "line-reader-private.h" @@ -47,6 +48,8 @@ struct _SysprofDocument GMappedFile *mapped_file; const guint8 *base; + SysprofStrings *strings; + GtkBitset *file_chunks; GtkBitset *traceables; GtkBitset *processes; @@ -58,9 +61,6 @@ struct _SysprofDocument SysprofMountNamespace *mount_namespace; - GMutex strings_mutex; - GHashTable *strings; - SysprofCaptureFileHeader header; guint needs_swap : 1; }; @@ -150,11 +150,12 @@ sysprof_document_finalize (GObject *object) { SysprofDocument *self = (SysprofDocument *)object; + g_clear_pointer (&self->strings, sysprof_strings_unref); + g_clear_pointer (&self->pid_to_mmaps, g_hash_table_unref); g_clear_pointer (&self->pid_to_mountinfo, g_hash_table_unref); g_clear_pointer (&self->mapped_file, g_mapped_file_unref); g_clear_pointer (&self->frames, g_array_unref); - g_clear_pointer (&self->strings, g_hash_table_unref); g_clear_pointer (&self->traceables, gtk_bitset_unref); g_clear_pointer (&self->processes, gtk_bitset_unref); @@ -165,8 +166,6 @@ sysprof_document_finalize (GObject *object) g_clear_pointer (&self->files_first_position, g_hash_table_unref); - g_mutex_clear (&self->strings_mutex); - G_OBJECT_CLASS (sysprof_document_parent_class)->finalize (object); } static void @@ -180,11 +179,9 @@ sysprof_document_class_init (SysprofDocumentClass *klass) static void sysprof_document_init (SysprofDocument *self) { - g_mutex_init (&self->strings_mutex); + self->strings = sysprof_strings_new (); self->frames = g_array_new (FALSE, FALSE, sizeof (SysprofDocumentFramePointer)); - self->strings = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, - (GDestroyNotify)g_ref_string_release); self->traceables = gtk_bitset_new_empty (); self->file_chunks = gtk_bitset_new_empty (); @@ -549,23 +546,9 @@ char * _sysprof_document_ref_string (SysprofDocument *self, const char *name) { - char *ret; - g_return_val_if_fail (SYSPROF_IS_DOCUMENT (self), NULL); - if (name == NULL) - return NULL; - - g_mutex_lock (&self->strings_mutex); - if (!(ret = g_hash_table_lookup (self->strings, name))) - { - ret = g_ref_string_new (name); - g_hash_table_insert (self->strings, ret, ret); - } - ret = g_ref_string_acquire (ret); - g_mutex_unlock (&self->strings_mutex); - - return ret; + return sysprof_strings_get (self->strings, name); } static void diff --git a/src/libsysprof-analyze/sysprof-strings-private.h b/src/libsysprof-analyze/sysprof-strings-private.h new file mode 100644 index 00000000..3d58b927 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-strings-private.h @@ -0,0 +1,37 @@ +/* sysprof-strings-private.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +typedef struct _SysprofStrings SysprofStrings; + +SysprofStrings *sysprof_strings_new (void); +SysprofStrings *sysprof_strings_ref (SysprofStrings *self); +void sysprof_strings_unref (SysprofStrings *self); +GRefString *sysprof_strings_get (SysprofStrings *self, + const char *string); +GRefString *sysprof_strings_lookup (SysprofStrings *self, + const char *string); + +G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-strings.c b/src/libsysprof-analyze/sysprof-strings.c new file mode 100644 index 00000000..319d4219 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-strings.c @@ -0,0 +1,96 @@ +/* sysprof-strings.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-strings-private.h" + +struct _SysprofStrings +{ + GMutex mutex; + GHashTable *hashtable; +}; + +SysprofStrings * +sysprof_strings_new (void) +{ + SysprofStrings *self; + + self = g_atomic_rc_box_new0 (SysprofStrings); + g_mutex_init (&self->mutex); + self->hashtable = g_hash_table_new_full (g_str_hash, + g_str_equal, + (GDestroyNotify)g_ref_string_release, + NULL); + + return self; +} + +SysprofStrings * +sysprof_strings_ref (SysprofStrings *self) +{ + return g_atomic_rc_box_acquire (self); +} + +static void +sysprof_strings_finalize (gpointer data) +{ + SysprofStrings *self = data; + + g_mutex_clear (&self->mutex); + g_clear_pointer (&self->hashtable, g_hash_table_unref); +} + +void +sysprof_strings_unref (SysprofStrings *self) +{ + g_atomic_rc_box_release_full (self, sysprof_strings_finalize); +} + +GRefString * +sysprof_strings_get (SysprofStrings *self, + const char *string) +{ + GRefString *ret; + + g_mutex_lock (&self->mutex); + if (!(ret = g_hash_table_lookup (self->hashtable, string))) + { + ret = g_ref_string_new (string); + g_hash_table_insert (self->hashtable, ret, ret); + } + g_ref_string_acquire (ret); + g_mutex_unlock (&self->mutex); + + return ret; +} + +GRefString * +sysprof_strings_lookup (SysprofStrings *self, + const char *string) +{ + GRefString *ret; + + g_mutex_lock (&self->mutex); + ret = g_hash_table_lookup (self->hashtable, string); + g_mutex_unlock (&self->mutex); + + return ret; +} From 0b0fe9f9039ebfbfcef97a5d801a3b387a20917d Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 10 May 2023 12:55:30 -0700 Subject: [PATCH 0095/1030] libsysprof-analyze: give mount access to strings in ctor --- src/libsysprof-analyze/sysprof-document.c | 6 +++++- .../sysprof-mount-private.h | 21 +++++++++++-------- src/libsysprof-analyze/sysprof-mount.c | 3 ++- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index bc2e6949..ced8e01a 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -354,9 +354,13 @@ sysprof_document_load_mountinfo (SysprofDocument *self, line[line_len] = 0; - if ((mount = sysprof_mount_new_for_mountinfo (line))) + if ((mount = sysprof_mount_new_for_mountinfo (self->strings, line))) sysprof_mount_namespace_add_mount (mount_namespace, g_steal_pointer (&mount)); } + + g_hash_table_insert (self->pid_to_mountinfo, + GINT_TO_POINTER (pid), + g_steal_pointer (&mount_namespace)); } static void diff --git a/src/libsysprof-analyze/sysprof-mount-private.h b/src/libsysprof-analyze/sysprof-mount-private.h index 081fef17..675972d6 100644 --- a/src/libsysprof-analyze/sysprof-mount-private.h +++ b/src/libsysprof-analyze/sysprof-mount-private.h @@ -22,20 +22,23 @@ #include +#include "sysprof-strings-private.h" + G_BEGIN_DECLS #define SYSPROF_TYPE_MOUNT (sysprof_mount_get_type()) G_DECLARE_FINAL_TYPE (SysprofMount, sysprof_mount, SYSPROF, MOUNT, GObject) -SysprofMount *sysprof_mount_new_for_mountinfo (const char *mountinfo); -int sysprof_mount_get_device_major (SysprofMount *self); -int sysprof_mount_get_device_minor (SysprofMount *self); -const char *sysprof_mount_get_root (SysprofMount *self); -const char *sysprof_mount_get_mount_path (SysprofMount *self); -const char *sysprof_mount_get_mount_source (SysprofMount *self); -const char *sysprof_mount_get_filesystem_type (SysprofMount *self); -const char *sysprof_mount_get_superblock_option (SysprofMount *self, - const char *option); +SysprofMount *sysprof_mount_new_for_mountinfo (SysprofStrings *strings, + const char *mountinfo); +int sysprof_mount_get_device_major (SysprofMount *self); +int sysprof_mount_get_device_minor (SysprofMount *self); +const char *sysprof_mount_get_root (SysprofMount *self); +const char *sysprof_mount_get_mount_path (SysprofMount *self); +const char *sysprof_mount_get_mount_source (SysprofMount *self); +const char *sysprof_mount_get_filesystem_type (SysprofMount *self); +const char *sysprof_mount_get_superblock_option (SysprofMount *self, + const char *option); G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-mount.c b/src/libsysprof-analyze/sysprof-mount.c index 786bf111..4d3bdf19 100644 --- a/src/libsysprof-analyze/sysprof-mount.c +++ b/src/libsysprof-analyze/sysprof-mount.c @@ -149,7 +149,8 @@ sysprof_mount_init (SysprofMount *self) } SysprofMount * -sysprof_mount_new_for_mountinfo (const char *mountinfo) +sysprof_mount_new_for_mountinfo (SysprofStrings *strings, + const char *mountinfo) { return NULL; } From b3a4c295c346074af181610028e8bd92bb0b5b84 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 10 May 2023 15:14:09 -0700 Subject: [PATCH 0096/1030] libsysprof-analyze: add basic symbol cache This relies on begin/end range for the symbols to create something akin to an interval tree, albeit with GSequence. If performance needs to be addressed, can probably augment SysprofSymbol for an interval rbtree. --- src/libsysprof-analyze/meson.build | 1 + .../sysprof-bundled-symbolizer.c | 4 +- .../sysprof-document-symbols.c | 24 ++- .../sysprof-symbol-cache-private.h | 39 +++++ src/libsysprof-analyze/sysprof-symbol-cache.c | 140 ++++++++++++++++++ .../sysprof-symbol-private.h | 20 ++- src/libsysprof-analyze/sysprof-symbol.c | 22 +-- 7 files changed, 229 insertions(+), 21 deletions(-) create mode 100644 src/libsysprof-analyze/sysprof-symbol-cache-private.h create mode 100644 src/libsysprof-analyze/sysprof-symbol-cache.c diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build index 9f150797..e8d71848 100644 --- a/src/libsysprof-analyze/meson.build +++ b/src/libsysprof-analyze/meson.build @@ -29,6 +29,7 @@ libsysprof_analyze_private_sources = [ 'sysprof-mount-device.c', 'sysprof-mount-namespace.c', 'sysprof-strings.c', + 'sysprof-symbol-cache.c', ] libsysprof_analyze_public_headers = [ diff --git a/src/libsysprof-analyze/sysprof-bundled-symbolizer.c b/src/libsysprof-analyze/sysprof-bundled-symbolizer.c index 2f88b6e6..279bdc2c 100644 --- a/src/libsysprof-analyze/sysprof-bundled-symbolizer.c +++ b/src/libsysprof-analyze/sysprof-bundled-symbolizer.c @@ -209,7 +209,9 @@ sysprof_bundled_symbolizer_symbolize (SysprofSymbolizer *symbolizer, if (ret->offset < (self->endptr - self->beginptr)) return _sysprof_symbol_new (g_ref_string_new (&self->beginptr[ret->offset]), g_steal_pointer (&tag), - NULL); + NULL, + ret->addr_begin, + ret->addr_end); return NULL; } diff --git a/src/libsysprof-analyze/sysprof-document-symbols.c b/src/libsysprof-analyze/sysprof-document-symbols.c index a275d2ac..1a203362 100644 --- a/src/libsysprof-analyze/sysprof-document-symbols.c +++ b/src/libsysprof-analyze/sysprof-document-symbols.c @@ -24,13 +24,14 @@ #include "sysprof-document-symbols-private.h" #include "sysprof-document-traceable.h" #include "sysprof-symbol-private.h" +#include "sysprof-symbol-cache-private.h" #include "sysprof-symbolizer-private.h" struct _SysprofDocumentSymbols { - GObject parent_instance; - + GObject parent_instance; SysprofSymbol *context_switches[SYSPROF_ADDRESS_CONTEXT_GUEST_USER+1]; + GHashTable *pid_to_symbol_cache; }; G_DEFINE_FINAL_TYPE (SysprofDocumentSymbols, sysprof_document_symbols, G_TYPE_OBJECT) @@ -43,6 +44,8 @@ sysprof_document_symbols_finalize (GObject *object) for (guint i = 0; i < G_N_ELEMENTS (self->context_switches); i++) g_clear_object (&self->context_switches[i]); + g_clear_pointer (&self->pid_to_symbol_cache, g_hash_table_unref); + G_OBJECT_CLASS (sysprof_document_symbols_parent_class)->finalize (object); } @@ -57,6 +60,7 @@ sysprof_document_symbols_class_init (SysprofDocumentSymbolsClass *klass) static void sysprof_document_symbols_init (SysprofDocumentSymbols *self) { + self->pid_to_symbol_cache = g_hash_table_new_full (NULL, NULL, NULL, g_object_unref); } typedef struct _Symbolize @@ -81,6 +85,7 @@ sysprof_document_symbols_add_traceable (SysprofDocumentSymbols *self, SysprofSymbolizer *symbolizer) { SysprofAddressContext last_context; + SysprofSymbolCache *symbol_cache; guint64 *addresses; guint n_addresses; gint64 time; @@ -93,6 +98,12 @@ sysprof_document_symbols_add_traceable (SysprofDocumentSymbols *self, time = sysprof_document_frame_get_time (SYSPROF_DOCUMENT_FRAME (traceable)); pid = sysprof_document_frame_get_pid (SYSPROF_DOCUMENT_FRAME (traceable)); + if (!(symbol_cache = g_hash_table_lookup (self->pid_to_symbol_cache, GINT_TO_POINTER (pid)))) + { + symbol_cache = sysprof_symbol_cache_new (); + g_hash_table_insert (self->pid_to_symbol_cache, GINT_TO_POINTER (pid), symbol_cache); + } + /* TODO: We need to get the SysprofMountNamespace for the PID which must have * already been compiled. We also need the list SysprofDocumentMmap so that we * can get the build-id or inode to do various validation checks. @@ -122,10 +133,17 @@ sysprof_document_symbols_add_traceable (SysprofDocumentSymbols *self, { last_context = context; } + else if (sysprof_symbol_cache_lookup (symbol_cache, address) != NULL) + { + continue; + } else { g_autoptr(SysprofSymbol) symbol = _sysprof_symbolizer_symbolize (symbolizer, time, pid, address); + if (symbol != NULL) + sysprof_symbol_cache_take (symbol_cache, g_steal_pointer (&symbol)); + /* TODO: This isn't the API we'll use for symbolizing, it just gets * some plumbing in place. Additionally, we'll probably cache all these * values here so that we can skip calling the symbolizer at all for @@ -173,7 +191,7 @@ sysprof_document_symbols_worker (GTask *task, for (guint cs = 0; cs < G_N_ELEMENTS (context_switches); cs++) { g_autoptr(GRefString) name = g_ref_string_new_intern (context_switches[cs].name); - g_autoptr(SysprofSymbol) symbol = _sysprof_symbol_new (name, NULL, NULL); + g_autoptr(SysprofSymbol) symbol = _sysprof_symbol_new (name, NULL, NULL, 0, 0); /* TODO: It would be nice if we had enough insight from the capture header * as to the host system, so we can show "vmlinuz" and "Linux" respectively diff --git a/src/libsysprof-analyze/sysprof-symbol-cache-private.h b/src/libsysprof-analyze/sysprof-symbol-cache-private.h new file mode 100644 index 00000000..c3842b52 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-symbol-cache-private.h @@ -0,0 +1,39 @@ +/* sysprof-symbol-cache-private.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +#include "sysprof-symbol.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_SYMBOL_CACHE (sysprof_symbol_cache_get_type()) + +G_DECLARE_FINAL_TYPE (SysprofSymbolCache, sysprof_symbol_cache, SYSPROF, SYMBOL_CACHE, GObject) + +SysprofSymbolCache *sysprof_symbol_cache_new (void); +SysprofSymbol *sysprof_symbol_cache_lookup (SysprofSymbolCache *self, + SysprofAddress address); +void sysprof_symbol_cache_take (SysprofSymbolCache *self, + SysprofSymbol *symbol); + +G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-symbol-cache.c b/src/libsysprof-analyze/sysprof-symbol-cache.c new file mode 100644 index 00000000..37f4dd88 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-symbol-cache.c @@ -0,0 +1,140 @@ +/* sysprof-symbol-cache.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-symbol-private.h" +#include "sysprof-symbol-cache-private.h" + +struct _SysprofSymbolCache +{ + GObject parent_instance; + GSequence *symbols; +}; + +G_DEFINE_FINAL_TYPE (SysprofSymbolCache, sysprof_symbol_cache, G_TYPE_OBJECT) + +static void +sysprof_symbol_cache_finalize (GObject *object) +{ + SysprofSymbolCache *self = (SysprofSymbolCache *)object; + + g_clear_pointer (&self->symbols, g_sequence_free); + + G_OBJECT_CLASS (sysprof_symbol_cache_parent_class)->finalize (object); +} + +static void +sysprof_symbol_cache_class_init (SysprofSymbolCacheClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = sysprof_symbol_cache_finalize; +} + +static void +sysprof_symbol_cache_init (SysprofSymbolCache *self) +{ + self->symbols = g_sequence_new (g_object_unref); +} + +SysprofSymbolCache * +sysprof_symbol_cache_new (void) +{ + return g_object_new (SYSPROF_TYPE_SYMBOL_CACHE, NULL); +} + +static int +sysprof_symbol_cache_compare (gconstpointer a, + gconstpointer b, + gpointer user_data) +{ + const SysprofSymbol *sym_a = a; + const SysprofSymbol *sym_b = b; + + if (sym_a->begin_address < sym_b->begin_address) + return -1; + + if (sym_a->begin_address > sym_b->end_address) + return 1; + + return 0; +} + +/** + * sysprof_symbol_cache_take: + * @self: a #SysprofSymbolCache + * @symbol: (transfer full): a #SysprofSymbol + * + */ +void +sysprof_symbol_cache_take (SysprofSymbolCache *self, + SysprofSymbol *symbol) +{ + g_return_if_fail (SYSPROF_IS_SYMBOL_CACHE (self)); + g_return_if_fail (SYSPROF_IS_SYMBOL (symbol)); + + if (symbol->begin_address == 0 || symbol->end_address == 0) + return; + + g_sequence_insert_sorted (self->symbols, + g_object_ref (symbol), + sysprof_symbol_cache_compare, + NULL); +} + +static int +sysprof_symbol_cache_lookup_func (gconstpointer a, + gconstpointer b, + gpointer user_data) +{ + const SysprofSymbol *sym_a = a; + const gint64 *addr = b; + + if (*addr < sym_a->begin_address) + return 1; + + if (*addr > sym_a->end_address) + return -1; + + return 0; +} + +SysprofSymbol * +sysprof_symbol_cache_lookup (SysprofSymbolCache *self, + SysprofAddress address) +{ + GSequenceIter *iter; + + g_return_val_if_fail (SYSPROF_IS_SYMBOL_CACHE (self), NULL); + + if (address == 0) + return NULL; + + iter = g_sequence_lookup (self->symbols, + &address, + sysprof_symbol_cache_lookup_func, + NULL); + + if (iter != NULL) + return g_sequence_get (iter); + + return NULL; +} diff --git a/src/libsysprof-analyze/sysprof-symbol-private.h b/src/libsysprof-analyze/sysprof-symbol-private.h index 8a7146d1..1af67980 100644 --- a/src/libsysprof-analyze/sysprof-symbol-private.h +++ b/src/libsysprof-analyze/sysprof-symbol-private.h @@ -25,8 +25,22 @@ G_BEGIN_DECLS -SysprofSymbol *_sysprof_symbol_new (GRefString *name, - GRefString *binary_nick, - GRefString *binary_path); +struct _SysprofSymbol +{ + GObject parent_instance; + + GRefString *name; + GRefString *binary_path; + GRefString *binary_nick; + + SysprofAddress begin_address; + SysprofAddress end_address; +}; + +SysprofSymbol *_sysprof_symbol_new (GRefString *name, + GRefString *binary_nick, + GRefString *binary_path, + SysprofAddress begin_address, + SysprofAddress end_address); G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-symbol.c b/src/libsysprof-analyze/sysprof-symbol.c index 8aef2561..719ffb85 100644 --- a/src/libsysprof-analyze/sysprof-symbol.c +++ b/src/libsysprof-analyze/sysprof-symbol.c @@ -21,17 +21,7 @@ #include "config.h" -#include "sysprof-symbol.h" - -struct _SysprofSymbol -{ - GObject parent_instance; - - /* All are GRefString */ - char *name; - char *binary_path; - char *binary_nick; -}; +#include "sysprof-symbol-private.h" G_DEFINE_FINAL_TYPE (SysprofSymbol, sysprof_symbol, G_TYPE_OBJECT) @@ -140,9 +130,11 @@ sysprof_symbol_get_binary_path (SysprofSymbol *self) } SysprofSymbol * -_sysprof_symbol_new (GRefString *name, - GRefString *binary_path, - GRefString *binary_nick) +_sysprof_symbol_new (GRefString *name, + GRefString *binary_path, + GRefString *binary_nick, + SysprofAddress begin_address, + SysprofAddress end_address) { SysprofSymbol *self; @@ -150,6 +142,8 @@ _sysprof_symbol_new (GRefString *name, self->name = name; self->binary_path = binary_path; self->binary_nick = binary_nick; + self->begin_address = begin_address; + self->end_address = end_address; return self; } From c72955e7d4d296b96c68bfdb3afbfd9b45f30083 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 10 May 2023 16:48:48 -0700 Subject: [PATCH 0097/1030] libsysprof-analyze: add address layout to contain mmap regions --- src/libsysprof-analyze/meson.build | 1 + .../sysprof-address-layout-private.h | 39 ++++++ .../sysprof-address-layout.c | 126 ++++++++++++++++++ 3 files changed, 166 insertions(+) create mode 100644 src/libsysprof-analyze/sysprof-address-layout-private.h create mode 100644 src/libsysprof-analyze/sysprof-address-layout.c diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build index e8d71848..c35e52ed 100644 --- a/src/libsysprof-analyze/meson.build +++ b/src/libsysprof-analyze/meson.build @@ -22,6 +22,7 @@ libsysprof_analyze_public_sources = [ ] libsysprof_analyze_private_sources = [ + 'sysprof-address-layout.c', 'sysprof-document-bitset-index.c', 'sysprof-mapped-file.c', 'sysprof-memory-map.c', diff --git a/src/libsysprof-analyze/sysprof-address-layout-private.h b/src/libsysprof-analyze/sysprof-address-layout-private.h new file mode 100644 index 00000000..15bce8d4 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-address-layout-private.h @@ -0,0 +1,39 @@ +/* sysprof-address-layout.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +#include "sysprof-document-mmap.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_ADDRESS_LAYOUT (sysprof_address_layout_get_type()) + +G_DECLARE_FINAL_TYPE (SysprofAddressLayout, sysprof_address_layout, SYSPROF, ADDRESS_LAYOUT, GObject) + +SysprofAddressLayout *sysprof_address_layout_new (void); +void sysprof_address_layout_take (SysprofAddressLayout *self, + SysprofDocumentMmap *map); +SysprofDocumentMmap *sysprof_address_layout_lookup (SysprofAddressLayout *self, + SysprofAddress address); + +G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-address-layout.c b/src/libsysprof-analyze/sysprof-address-layout.c new file mode 100644 index 00000000..53811d7d --- /dev/null +++ b/src/libsysprof-analyze/sysprof-address-layout.c @@ -0,0 +1,126 @@ +/* sysprof-address-layout.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-address-layout-private.h" + +struct _SysprofAddressLayout +{ + GObject parent_instance; + GSequence *mmaps; +}; + +G_DEFINE_FINAL_TYPE (SysprofAddressLayout, sysprof_address_layout, G_TYPE_OBJECT) + +static void +sysprof_address_layout_finalize (GObject *object) +{ + SysprofAddressLayout *self = (SysprofAddressLayout *)object; + + g_clear_pointer (&self->mmaps, g_sequence_free); + + G_OBJECT_CLASS (sysprof_address_layout_parent_class)->finalize (object); +} + +static void +sysprof_address_layout_class_init (SysprofAddressLayoutClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = sysprof_address_layout_finalize; +} + +static void +sysprof_address_layout_init (SysprofAddressLayout *self) +{ + self->mmaps = g_sequence_new (g_object_unref); +} + +SysprofAddressLayout * +sysprof_address_layout_new (void) +{ + return g_object_new (SYSPROF_TYPE_ADDRESS_LAYOUT, NULL); +} + +static int +sysprof_address_layout_compare (gconstpointer a, + gconstpointer b, + gpointer user_data) +{ + SysprofDocumentMmap *mmap_a = (SysprofDocumentMmap *)a; + SysprofDocumentMmap *mmap_b = (SysprofDocumentMmap *)b; + guint64 begin_a = sysprof_document_mmap_get_start_address (mmap_a); + guint64 begin_b = sysprof_document_mmap_get_start_address (mmap_b); + + if (begin_a < begin_b) + return -1; + + if (begin_a > begin_b) + return 1; + + return 0; +} + +void +sysprof_address_layout_take (SysprofAddressLayout *self, + SysprofDocumentMmap *map) +{ + g_return_if_fail (SYSPROF_IS_ADDRESS_LAYOUT (self)); + g_return_if_fail (SYSPROF_IS_DOCUMENT_MMAP (map)); + + g_sequence_insert_sorted (self->mmaps, + map, + sysprof_address_layout_compare, + NULL); +} + +static int +sysprof_address_layout_lookup_func (gconstpointer a, + gconstpointer b, + gpointer user_data) +{ + SysprofDocumentMmap *mmap_a = (SysprofDocumentMmap *)a; + const gint64 *addr = b; + + if (*addr < sysprof_document_mmap_get_start_address (mmap_a)) + return 1; + + if (*addr >= sysprof_document_mmap_get_end_address (mmap_a)) + return -1; + + return 0; +} + +SysprofDocumentMmap * +sysprof_address_layout_lookup (SysprofAddressLayout *self, + SysprofAddress address) +{ + GSequenceIter *iter; + + g_return_val_if_fail (SYSPROF_IS_ADDRESS_LAYOUT (self), NULL); + + iter = g_sequence_lookup (self->mmaps, + &address, + sysprof_address_layout_lookup_func, + NULL); + + return iter ? g_sequence_get (iter) : NULL; +} From 8f4fa95663a4b866b38ac79a65ba88b0be6b5302 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 10 May 2023 16:51:05 -0700 Subject: [PATCH 0098/1030] libsysprof-analyze: use mountnamespace/addresslayout in symbolize --- .../sysprof-bundled-symbolizer.c | 9 +-- .../sysprof-document-symbols-private.h | 2 + .../sysprof-document-symbols.c | 64 ++++++++----------- src/libsysprof-analyze/sysprof-document.c | 61 +++++++----------- .../sysprof-multi-symbolizer.c | 11 ++-- .../sysprof-symbolizer-private.h | 52 ++++++++------- src/libsysprof-analyze/sysprof-symbolizer.c | 11 ++-- 7 files changed, 97 insertions(+), 113 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-bundled-symbolizer.c b/src/libsysprof-analyze/sysprof-bundled-symbolizer.c index 279bdc2c..69bc825a 100644 --- a/src/libsysprof-analyze/sysprof-bundled-symbolizer.c +++ b/src/libsysprof-analyze/sysprof-bundled-symbolizer.c @@ -169,10 +169,11 @@ search_for_symbol_cb (gconstpointer a, } static SysprofSymbol * -sysprof_bundled_symbolizer_symbolize (SysprofSymbolizer *symbolizer, - gint64 time, - int pid, - SysprofAddress address) +sysprof_bundled_symbolizer_symbolize (SysprofSymbolizer *symbolizer, + SysprofMountNamespace *mount_namespace, + SysprofAddressLayout *address_layout, + int pid, + SysprofAddress address) { SysprofBundledSymbolizer *self = SYSPROF_BUNDLED_SYMBOLIZER (symbolizer); g_autoptr(GRefString) tag = NULL; diff --git a/src/libsysprof-analyze/sysprof-document-symbols-private.h b/src/libsysprof-analyze/sysprof-document-symbols-private.h index ad76c901..36e0ca94 100644 --- a/src/libsysprof-analyze/sysprof-document-symbols-private.h +++ b/src/libsysprof-analyze/sysprof-document-symbols-private.h @@ -27,6 +27,8 @@ G_BEGIN_DECLS void _sysprof_document_symbols_new (SysprofDocument *document, SysprofSymbolizer *symbolizer, + GHashTable *pid_to_mount_namespaces, + GHashTable *pid_to_address_layouts, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); diff --git a/src/libsysprof-analyze/sysprof-document-symbols.c b/src/libsysprof-analyze/sysprof-document-symbols.c index 1a203362..17b2fa22 100644 --- a/src/libsysprof-analyze/sysprof-document-symbols.c +++ b/src/libsysprof-analyze/sysprof-document-symbols.c @@ -20,9 +20,11 @@ #include "config.h" +#include "sysprof-address-layout-private.h" #include "sysprof-document-private.h" #include "sysprof-document-symbols-private.h" #include "sysprof-document-traceable.h" +#include "sysprof-mount-namespace-private.h" #include "sysprof-symbol-private.h" #include "sysprof-symbol-cache-private.h" #include "sysprof-symbolizer-private.h" @@ -68,6 +70,8 @@ typedef struct _Symbolize SysprofDocument *document; SysprofSymbolizer *symbolizer; SysprofDocumentSymbols *symbols; + GHashTable *pid_to_address_layouts; + GHashTable *pid_to_mount_namespaces; } Symbolize; static void @@ -76,48 +80,29 @@ symbolize_free (Symbolize *state) g_clear_object (&state->document); g_clear_object (&state->symbolizer); g_clear_object (&state->symbols); + g_clear_pointer (&state->pid_to_address_layouts, g_hash_table_unref); + g_clear_pointer (&state->pid_to_mount_namespaces, g_hash_table_unref); g_free (state); } static void sysprof_document_symbols_add_traceable (SysprofDocumentSymbols *self, SysprofDocumentTraceable *traceable, - SysprofSymbolizer *symbolizer) + SysprofSymbolizer *symbolizer, + SysprofMountNamespace *mount_namespace, + SysprofAddressLayout *address_layout, + SysprofSymbolCache *symbol_cache) { SysprofAddressContext last_context; - SysprofSymbolCache *symbol_cache; guint64 *addresses; guint n_addresses; - gint64 time; int pid; g_assert (SYSPROF_IS_DOCUMENT_SYMBOLS (self)); g_assert (SYSPROF_IS_DOCUMENT_TRACEABLE (traceable)); g_assert (SYSPROF_IS_SYMBOLIZER (symbolizer)); - time = sysprof_document_frame_get_time (SYSPROF_DOCUMENT_FRAME (traceable)); pid = sysprof_document_frame_get_pid (SYSPROF_DOCUMENT_FRAME (traceable)); - - if (!(symbol_cache = g_hash_table_lookup (self->pid_to_symbol_cache, GINT_TO_POINTER (pid)))) - { - symbol_cache = sysprof_symbol_cache_new (); - g_hash_table_insert (self->pid_to_symbol_cache, GINT_TO_POINTER (pid), symbol_cache); - } - - /* TODO: We need to get the SysprofMountNamespace for the PID which must have - * already been compiled. We also need the list SysprofDocumentMmap so that we - * can get the build-id or inode to do various validation checks. - * - * It would be nice if that was all pre-compiled by time we get here and also - * re-usable on the SysprofDocument for other tooling that may need to access - * that. - * - * The symbolizer will need to gain API to use those too. - * - * Some of this will need to be done asynchronously as well because decoding - * from debuginfod could be quite costly in disk/network/time. - */ - n_addresses = sysprof_document_traceable_get_stack_depth (traceable); addresses = g_alloca (sizeof (guint64) * n_addresses); sysprof_document_traceable_get_stack_addresses (traceable, addresses, n_addresses); @@ -139,16 +124,10 @@ sysprof_document_symbols_add_traceable (SysprofDocumentSymbols *self, } else { - g_autoptr(SysprofSymbol) symbol = _sysprof_symbolizer_symbolize (symbolizer, time, pid, address); + g_autoptr(SysprofSymbol) symbol = _sysprof_symbolizer_symbolize (symbolizer, mount_namespace, address_layout, pid, address); if (symbol != NULL) sysprof_symbol_cache_take (symbol_cache, g_steal_pointer (&symbol)); - - /* TODO: This isn't the API we'll use for symbolizing, it just gets - * some plumbing in place. Additionally, we'll probably cache all these - * values here so that we can skip calling the symbolizer at all for - * subsequent symbols within a given range. - */ } } } @@ -207,11 +186,20 @@ sysprof_document_symbols_worker (GTask *task, do { g_autoptr(SysprofDocumentTraceable) traceable = g_list_model_get_item (model, i); + int pid = sysprof_document_frame_get_pid (SYSPROF_DOCUMENT_FRAME (traceable)); + SysprofMountNamespace *mount_namespace = g_hash_table_lookup (state->pid_to_mount_namespaces, GINT_TO_POINTER (pid)); + SysprofAddressLayout *address_layout = g_hash_table_lookup (state->pid_to_address_layouts, GINT_TO_POINTER (pid)); + SysprofSymbolCache *symbol_cache = g_hash_table_lookup (state->symbols->pid_to_symbol_cache, GINT_TO_POINTER (pid)); - if (SYSPROF_IS_DOCUMENT_TRACEABLE (traceable)) - sysprof_document_symbols_add_traceable (state->symbols, - traceable, - state->symbolizer); + if (symbol_cache == NULL) + { + symbol_cache = sysprof_symbol_cache_new (); + g_hash_table_insert (state->symbols->pid_to_symbol_cache, + GINT_TO_POINTER (pid), + symbol_cache); + } + + sysprof_document_symbols_add_traceable (state->symbols, traceable, state->symbolizer, mount_namespace, address_layout, symbol_cache); } while (gtk_bitset_iter_next (&iter, &i)); } @@ -224,6 +212,8 @@ sysprof_document_symbols_worker (GTask *task, void _sysprof_document_symbols_new (SysprofDocument *document, SysprofSymbolizer *symbolizer, + GHashTable *pid_to_mount_namespaces, + GHashTable *pid_to_address_layouts, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) @@ -238,6 +228,8 @@ _sysprof_document_symbols_new (SysprofDocument *document, state->document = g_object_ref (document); state->symbolizer = g_object_ref (symbolizer); state->symbols = g_object_new (SYSPROF_TYPE_DOCUMENT_SYMBOLS, NULL); + state->pid_to_address_layouts = g_hash_table_ref (pid_to_address_layouts); + state->pid_to_mount_namespaces = g_hash_table_ref (pid_to_mount_namespaces); task = g_task_new (NULL, cancellable, callback, user_data); g_task_set_source_tag (task, _sysprof_document_symbols_new); diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index ced8e01a..beb8791e 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -56,8 +56,8 @@ struct _SysprofDocument GtkBitset *mmaps; GHashTable *files_first_position; - GHashTable *pid_to_mmaps; - GHashTable *pid_to_mountinfo; + GHashTable *pid_to_address_layouts; + GHashTable *pid_to_mount_namespaces; SysprofMountNamespace *mount_namespace; @@ -152,8 +152,8 @@ sysprof_document_finalize (GObject *object) g_clear_pointer (&self->strings, sysprof_strings_unref); - g_clear_pointer (&self->pid_to_mmaps, g_hash_table_unref); - g_clear_pointer (&self->pid_to_mountinfo, g_hash_table_unref); + g_clear_pointer (&self->pid_to_address_layouts, g_hash_table_unref); + g_clear_pointer (&self->pid_to_mount_namespaces, g_hash_table_unref); g_clear_pointer (&self->mapped_file, g_mapped_file_unref); g_clear_pointer (&self->frames, g_array_unref); @@ -189,35 +189,16 @@ sysprof_document_init (SysprofDocument *self) self->mmaps = gtk_bitset_new_empty (); self->files_first_position = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - self->pid_to_mmaps = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_ptr_array_unref); - self->pid_to_mountinfo = g_hash_table_new_full (NULL, NULL, NULL, g_object_unref); + self->pid_to_address_layouts = g_hash_table_new_full (NULL, NULL, NULL, g_object_unref); + self->pid_to_mount_namespaces = g_hash_table_new_full (NULL, NULL, NULL, g_object_unref); self->mount_namespace = sysprof_mount_namespace_new (); } -static int -compare_map (gconstpointer a, - gconstpointer b) -{ - SysprofDocumentMmap * const *mmap_a = a; - SysprofDocumentMmap * const *mmap_b = b; - SysprofAddress addr_a = sysprof_document_mmap_get_start_address (*mmap_a); - SysprofAddress addr_b = sysprof_document_mmap_get_start_address (*mmap_b); - - if (addr_a < addr_b) - return -1; - else if (addr_a > addr_b) - return 1; - else - return 0; -} - static void sysprof_document_load_memory_maps (SysprofDocument *self) { GtkBitsetIter iter; - GHashTableIter hiter; - gpointer key, value; guint i; g_assert (SYSPROF_IS_DOCUMENT (self)); @@ -228,25 +209,18 @@ sysprof_document_load_memory_maps (SysprofDocument *self) { g_autoptr(SysprofDocumentMmap) map = sysprof_document_get_item ((GListModel *)self, i); int pid = sysprof_document_frame_get_pid (SYSPROF_DOCUMENT_FRAME (map)); - GPtrArray *maps = g_hash_table_lookup (self->pid_to_mmaps, GINT_TO_POINTER (pid)); + SysprofAddressLayout *address_layout = g_hash_table_lookup (self->pid_to_address_layouts, GINT_TO_POINTER (pid)); - if G_UNLIKELY (maps == NULL) + if G_UNLIKELY (address_layout == NULL) { - maps = g_ptr_array_new_with_free_func (g_object_unref); - g_hash_table_insert (self->pid_to_mmaps, GINT_TO_POINTER (pid), maps); + address_layout = sysprof_address_layout_new (); + g_hash_table_insert (self->pid_to_address_layouts, GINT_TO_POINTER (pid), address_layout); } - g_ptr_array_add (maps, g_steal_pointer (&map)); + sysprof_address_layout_take (address_layout, g_steal_pointer (&map)); } while (gtk_bitset_iter_next (&iter, &i)); } - - g_hash_table_iter_init (&hiter, self->pid_to_mmaps); - while (g_hash_table_iter_next (&hiter, &key, &value)) - { - GPtrArray *ar = value; - g_ptr_array_sort (ar, compare_map); - } } static void @@ -358,7 +332,7 @@ sysprof_document_load_mountinfo (SysprofDocument *self, sysprof_mount_namespace_add_mount (mount_namespace, g_steal_pointer (&mount)); } - g_hash_table_insert (self->pid_to_mountinfo, + g_hash_table_insert (self->pid_to_mount_namespaces, GINT_TO_POINTER (pid), g_steal_pointer (&mount_namespace)); } @@ -371,7 +345,7 @@ sysprof_document_load_mountinfos (SysprofDocument *self) g_assert (SYSPROF_IS_DOCUMENT (self)); - g_hash_table_iter_init (&hiter, self->pid_to_mmaps); + g_hash_table_iter_init (&hiter, self->pid_to_address_layouts); while (g_hash_table_iter_next (&hiter, &key, &value)) { g_autoptr(SysprofMountNamespace) mount_namespace = sysprof_mount_namespace_new (); @@ -581,16 +555,25 @@ sysprof_document_symbolize_prepare_cb (GObject *object, SysprofSymbolizer *symbolizer = (SysprofSymbolizer *)object; g_autoptr(GTask) task = user_data; g_autoptr(GError) error = NULL; + SysprofDocument *self; g_assert (SYSPROF_IS_SYMBOLIZER (symbolizer)); g_assert (G_IS_ASYNC_RESULT (result)); g_assert (G_IS_TASK (task)); + self = g_task_get_source_object (task); + + g_assert (self != NULL); + g_assert (SYSPROF_IS_DOCUMENT (self)); + g_assert (self->pid_to_mount_namespaces != NULL); + if (!_sysprof_symbolizer_prepare_finish (symbolizer, result, &error)) g_task_return_error (task, g_steal_pointer (&error)); else _sysprof_document_symbols_new (g_task_get_source_object (task), symbolizer, + self->pid_to_mount_namespaces, + self->pid_to_address_layouts, g_task_get_cancellable (task), sysprof_document_symbolize_symbols_cb, g_object_ref (task)); diff --git a/src/libsysprof-analyze/sysprof-multi-symbolizer.c b/src/libsysprof-analyze/sysprof-multi-symbolizer.c index ffcc1475..b6f39d12 100644 --- a/src/libsysprof-analyze/sysprof-multi-symbolizer.c +++ b/src/libsysprof-analyze/sysprof-multi-symbolizer.c @@ -118,17 +118,18 @@ sysprof_multi_symbolizer_prepare_finish (SysprofSymbolizer *symbolizer, } static SysprofSymbol * -sysprof_multi_symbolizer_symbolize (SysprofSymbolizer *symbolizer, - gint64 time, - int pid, - SysprofAddress address) +sysprof_multi_symbolizer_symbolize (SysprofSymbolizer *symbolizer, + SysprofMountNamespace *mount_namespace, + SysprofAddressLayout *address_layout, + int pid, + SysprofAddress address) { SysprofMultiSymbolizer *self = SYSPROF_MULTI_SYMBOLIZER (symbolizer); for (guint i = 0; i < self->symbolizers->len; i++) { SysprofSymbolizer *child = g_ptr_array_index (self->symbolizers, i); - SysprofSymbol *symbol = _sysprof_symbolizer_symbolize (child, time, pid, address); + SysprofSymbol *symbol = _sysprof_symbolizer_symbolize (child, mount_namespace, address_layout, pid, address); if (symbol != NULL) return symbol; diff --git a/src/libsysprof-analyze/sysprof-symbolizer-private.h b/src/libsysprof-analyze/sysprof-symbolizer-private.h index 0416fb29..ad89fa12 100644 --- a/src/libsysprof-analyze/sysprof-symbolizer-private.h +++ b/src/libsysprof-analyze/sysprof-symbolizer-private.h @@ -20,9 +20,11 @@ #pragma once +#include "sysprof-address-layout-private.h" #include "sysprof-document.h" #include "sysprof-symbol.h" #include "sysprof-symbolizer.h" +#include "sysprof-mount-namespace-private.h" G_BEGIN_DECLS @@ -37,32 +39,34 @@ struct _SysprofSymbolizerClass { GObjectClass parent_class; - void (*prepare_async) (SysprofSymbolizer *self, - SysprofDocument *document, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); - gboolean (*prepare_finish) (SysprofSymbolizer *self, - GAsyncResult *result, - GError **error); - SysprofSymbol *(*symbolize) (SysprofSymbolizer *self, - gint64 time, - int pid, - SysprofAddress address); + void (*prepare_async) (SysprofSymbolizer *self, + SysprofDocument *document, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + gboolean (*prepare_finish) (SysprofSymbolizer *self, + GAsyncResult *result, + GError **error); + SysprofSymbol *(*symbolize) (SysprofSymbolizer *self, + SysprofMountNamespace *mount_namespace, + SysprofAddressLayout *address_layout, + int pid, + SysprofAddress address); }; -void _sysprof_symbolizer_prepare_async (SysprofSymbolizer *self, - SysprofDocument *document, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean _sysprof_symbolizer_prepare_finish (SysprofSymbolizer *self, - GAsyncResult *result, - GError **error); -SysprofSymbol *_sysprof_symbolizer_symbolize (SysprofSymbolizer *self, - gint64 time, - int pid, - SysprofAddress address); +void _sysprof_symbolizer_prepare_async (SysprofSymbolizer *self, + SysprofDocument *document, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean _sysprof_symbolizer_prepare_finish (SysprofSymbolizer *self, + GAsyncResult *result, + GError **error); +SysprofSymbol *_sysprof_symbolizer_symbolize (SysprofSymbolizer *self, + SysprofMountNamespace *mount_namespace, + SysprofAddressLayout *address_layout, + int pid, + SysprofAddress address); G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-symbolizer.c b/src/libsysprof-analyze/sysprof-symbolizer.c index 904d08ae..f1f148a6 100644 --- a/src/libsysprof-analyze/sysprof-symbolizer.c +++ b/src/libsysprof-analyze/sysprof-symbolizer.c @@ -91,10 +91,11 @@ _sysprof_symbolizer_prepare_finish (SysprofSymbolizer *self, } SysprofSymbol * -_sysprof_symbolizer_symbolize (SysprofSymbolizer *self, - gint64 time, - int pid, - SysprofAddress address) +_sysprof_symbolizer_symbolize (SysprofSymbolizer *self, + SysprofMountNamespace *mount_namespace, + SysprofAddressLayout *address_layout, + int pid, + SysprofAddress address) { - return SYSPROF_SYMBOLIZER_GET_CLASS (self)->symbolize (self, time, pid, address); + return SYSPROF_SYMBOLIZER_GET_CLASS (self)->symbolize (self, mount_namespace, address_layout, pid, address); } From ccd790fef569f6735725e4920a7c29ee1420cf50 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 11 May 2023 11:41:17 -0700 Subject: [PATCH 0099/1030] libsysprof-analyze: make AddressLayout a ListModel --- .../sysprof-address-layout.c | 38 ++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/src/libsysprof-analyze/sysprof-address-layout.c b/src/libsysprof-analyze/sysprof-address-layout.c index 53811d7d..163a862d 100644 --- a/src/libsysprof-analyze/sysprof-address-layout.c +++ b/src/libsysprof-analyze/sysprof-address-layout.c @@ -20,6 +20,8 @@ #include "config.h" +#include + #include "sysprof-address-layout-private.h" struct _SysprofAddressLayout @@ -28,7 +30,41 @@ struct _SysprofAddressLayout GSequence *mmaps; }; -G_DEFINE_FINAL_TYPE (SysprofAddressLayout, sysprof_address_layout, G_TYPE_OBJECT) +static guint +sysprof_address_layout_get_n_items (GListModel *model) +{ + return g_sequence_get_length (SYSPROF_ADDRESS_LAYOUT (model)->mmaps); +} + +static GType +sysprof_address_layout_get_item_type (GListModel *model) +{ + return SYSPROF_TYPE_DOCUMENT_MMAP; +} + +static gpointer +sysprof_address_layout_get_item (GListModel *model, + guint position) +{ + SysprofAddressLayout *self = SYSPROF_ADDRESS_LAYOUT (model); + GSequenceIter *iter = g_sequence_get_iter_at_pos (self->mmaps, position); + + if (g_sequence_iter_is_end (iter)) + return NULL; + + return g_object_ref (g_sequence_get (iter)); +} + +static void +list_model_iface_init (GListModelInterface *iface) +{ + iface->get_n_items = sysprof_address_layout_get_n_items; + iface->get_item = sysprof_address_layout_get_item; + iface->get_item_type = sysprof_address_layout_get_item_type; +} + +G_DEFINE_FINAL_TYPE_WITH_CODE (SysprofAddressLayout, sysprof_address_layout, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init)) static void sysprof_address_layout_finalize (GObject *object) From 9b5e25037b0a6f207aca4cf1e47f4d520ea59c86 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 11 May 2023 12:21:32 -0700 Subject: [PATCH 0100/1030] libsysprof-analyze: add SysprofProcessInfo This internal type is used to collect things about a process like the memory maps, address layout, and symbol cache. This can persist once parsed at startup, then applied to objects created on demand such as the SysprofDocumentProcess or used by symbolizers internally rather than complicated function arguments. --- src/libsysprof-analyze/meson.build | 1 + .../sysprof-bundled-symbolizer.c | 10 +-- .../sysprof-document-process-private.h | 31 +++++++ .../sysprof-document-process.c | 27 +++++- .../sysprof-document-symbols-private.h | 4 +- .../sysprof-document-symbols.c | 51 +++++------- src/libsysprof-analyze/sysprof-document.c | 82 ++++++++++++------- .../sysprof-multi-symbolizer.c | 10 +-- .../sysprof-process-info-private.h | 42 ++++++++++ src/libsysprof-analyze/sysprof-process-info.c | 67 +++++++++++++++ .../sysprof-symbolizer-private.h | 49 ++++++----- src/libsysprof-analyze/sysprof-symbolizer.c | 10 +-- 12 files changed, 275 insertions(+), 109 deletions(-) create mode 100644 src/libsysprof-analyze/sysprof-document-process-private.h create mode 100644 src/libsysprof-analyze/sysprof-process-info-private.h create mode 100644 src/libsysprof-analyze/sysprof-process-info.c diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build index c35e52ed..ae7d037d 100644 --- a/src/libsysprof-analyze/meson.build +++ b/src/libsysprof-analyze/meson.build @@ -29,6 +29,7 @@ libsysprof_analyze_private_sources = [ 'sysprof-mount.c', 'sysprof-mount-device.c', 'sysprof-mount-namespace.c', + 'sysprof-process-info.c', 'sysprof-strings.c', 'sysprof-symbol-cache.c', ] diff --git a/src/libsysprof-analyze/sysprof-bundled-symbolizer.c b/src/libsysprof-analyze/sysprof-bundled-symbolizer.c index 69bc825a..441f4576 100644 --- a/src/libsysprof-analyze/sysprof-bundled-symbolizer.c +++ b/src/libsysprof-analyze/sysprof-bundled-symbolizer.c @@ -169,11 +169,9 @@ search_for_symbol_cb (gconstpointer a, } static SysprofSymbol * -sysprof_bundled_symbolizer_symbolize (SysprofSymbolizer *symbolizer, - SysprofMountNamespace *mount_namespace, - SysprofAddressLayout *address_layout, - int pid, - SysprofAddress address) +sysprof_bundled_symbolizer_symbolize (SysprofSymbolizer *symbolizer, + const SysprofProcessInfo *process_info, + SysprofAddress address) { SysprofBundledSymbolizer *self = SYSPROF_BUNDLED_SYMBOLIZER (symbolizer); g_autoptr(GRefString) tag = NULL; @@ -181,7 +179,7 @@ sysprof_bundled_symbolizer_symbolize (SysprofSymbolizer *symbolizer, const Decoded key = { .addr_begin = address, .addr_end = address, - .pid = pid, + .pid = process_info->pid, .offset = 0, .tag_offset = 0, }; diff --git a/src/libsysprof-analyze/sysprof-document-process-private.h b/src/libsysprof-analyze/sysprof-document-process-private.h new file mode 100644 index 00000000..66d7e532 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-document-process-private.h @@ -0,0 +1,31 @@ +/* sysprof-document-process-private.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-document-process.h" +#include "sysprof-process-info-private.h" + +G_BEGIN_DECLS + +void _sysprof_document_process_set_info (SysprofDocumentProcess *self, + SysprofProcessInfo *process_info); + +G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-document-process.c b/src/libsysprof-analyze/sysprof-document-process.c index d307616b..070e49ab 100644 --- a/src/libsysprof-analyze/sysprof-document-process.c +++ b/src/libsysprof-analyze/sysprof-document-process.c @@ -21,11 +21,12 @@ #include "config.h" #include "sysprof-document-frame-private.h" -#include "sysprof-document-process.h" +#include "sysprof-document-process-private.h" struct _SysprofDocumentProcess { - SysprofDocumentFrame parent_instance; + SysprofDocumentFrame parent_instance; + SysprofProcessInfo *process_info; }; struct _SysprofDocumentProcessClass @@ -43,6 +44,16 @@ G_DEFINE_FINAL_TYPE (SysprofDocumentProcess, sysprof_document_process, SYSPROF_T static GParamSpec *properties [N_PROPS]; +static void +sysprof_document_process_finalize (GObject *object) +{ + SysprofDocumentProcess *self = (SysprofDocumentProcess *)object; + + g_clear_pointer (&self->process_info, sysprof_process_info_unref); + + G_OBJECT_CLASS (sysprof_document_process_parent_class)->finalize (object); +} + static void sysprof_document_process_get_property (GObject *object, guint prop_id, @@ -67,6 +78,7 @@ sysprof_document_process_class_init (SysprofDocumentProcessClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); + object_class->finalize = sysprof_document_process_finalize; object_class->get_property = sysprof_document_process_get_property; properties [PROP_COMMAND_LINE] = @@ -93,3 +105,14 @@ sysprof_document_process_get_command_line (SysprofDocumentProcess *self) return SYSPROF_DOCUMENT_FRAME_CSTRING (self, proc->cmdline); } + +void +_sysprof_document_process_set_info (SysprofDocumentProcess *self, + SysprofProcessInfo *process_info) +{ + g_return_if_fail (SYSPROF_IS_DOCUMENT_PROCESS (self)); + g_return_if_fail (process_info != NULL); + g_return_if_fail (self->process_info == NULL); + + self->process_info = sysprof_process_info_ref (process_info); +} diff --git a/src/libsysprof-analyze/sysprof-document-symbols-private.h b/src/libsysprof-analyze/sysprof-document-symbols-private.h index 36e0ca94..2dffd414 100644 --- a/src/libsysprof-analyze/sysprof-document-symbols-private.h +++ b/src/libsysprof-analyze/sysprof-document-symbols-private.h @@ -22,13 +22,13 @@ #include "sysprof-document.h" #include "sysprof-document-symbols.h" +#include "sysprof-process-info-private.h" G_BEGIN_DECLS void _sysprof_document_symbols_new (SysprofDocument *document, SysprofSymbolizer *symbolizer, - GHashTable *pid_to_mount_namespaces, - GHashTable *pid_to_address_layouts, + GHashTable *pid_to_process_info, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); diff --git a/src/libsysprof-analyze/sysprof-document-symbols.c b/src/libsysprof-analyze/sysprof-document-symbols.c index 17b2fa22..926d4274 100644 --- a/src/libsysprof-analyze/sysprof-document-symbols.c +++ b/src/libsysprof-analyze/sysprof-document-symbols.c @@ -31,9 +31,8 @@ struct _SysprofDocumentSymbols { - GObject parent_instance; + GObject parent_instance; SysprofSymbol *context_switches[SYSPROF_ADDRESS_CONTEXT_GUEST_USER+1]; - GHashTable *pid_to_symbol_cache; }; G_DEFINE_FINAL_TYPE (SysprofDocumentSymbols, sysprof_document_symbols, G_TYPE_OBJECT) @@ -46,8 +45,6 @@ sysprof_document_symbols_finalize (GObject *object) for (guint i = 0; i < G_N_ELEMENTS (self->context_switches); i++) g_clear_object (&self->context_switches[i]); - g_clear_pointer (&self->pid_to_symbol_cache, g_hash_table_unref); - G_OBJECT_CLASS (sysprof_document_symbols_parent_class)->finalize (object); } @@ -62,7 +59,6 @@ sysprof_document_symbols_class_init (SysprofDocumentSymbolsClass *klass) static void sysprof_document_symbols_init (SysprofDocumentSymbols *self) { - self->pid_to_symbol_cache = g_hash_table_new_full (NULL, NULL, NULL, g_object_unref); } typedef struct _Symbolize @@ -70,8 +66,7 @@ typedef struct _Symbolize SysprofDocument *document; SysprofSymbolizer *symbolizer; SysprofDocumentSymbols *symbols; - GHashTable *pid_to_address_layouts; - GHashTable *pid_to_mount_namespaces; + GHashTable *pid_to_process_info; } Symbolize; static void @@ -80,18 +75,15 @@ symbolize_free (Symbolize *state) g_clear_object (&state->document); g_clear_object (&state->symbolizer); g_clear_object (&state->symbols); - g_clear_pointer (&state->pid_to_address_layouts, g_hash_table_unref); - g_clear_pointer (&state->pid_to_mount_namespaces, g_hash_table_unref); + g_clear_pointer (&state->pid_to_process_info, g_hash_table_unref); g_free (state); } static void sysprof_document_symbols_add_traceable (SysprofDocumentSymbols *self, + SysprofProcessInfo *process_info, SysprofDocumentTraceable *traceable, - SysprofSymbolizer *symbolizer, - SysprofMountNamespace *mount_namespace, - SysprofAddressLayout *address_layout, - SysprofSymbolCache *symbol_cache) + SysprofSymbolizer *symbolizer) { SysprofAddressContext last_context; guint64 *addresses; @@ -99,6 +91,7 @@ sysprof_document_symbols_add_traceable (SysprofDocumentSymbols *self, int pid; g_assert (SYSPROF_IS_DOCUMENT_SYMBOLS (self)); + g_assert (process_info != NULL); g_assert (SYSPROF_IS_DOCUMENT_TRACEABLE (traceable)); g_assert (SYSPROF_IS_SYMBOLIZER (symbolizer)); @@ -118,16 +111,16 @@ sysprof_document_symbols_add_traceable (SysprofDocumentSymbols *self, { last_context = context; } - else if (sysprof_symbol_cache_lookup (symbol_cache, address) != NULL) + else if (sysprof_symbol_cache_lookup (process_info->symbol_cache, address) != NULL) { continue; } else { - g_autoptr(SysprofSymbol) symbol = _sysprof_symbolizer_symbolize (symbolizer, mount_namespace, address_layout, pid, address); + g_autoptr(SysprofSymbol) symbol = _sysprof_symbolizer_symbolize (symbolizer, process_info, address); if (symbol != NULL) - sysprof_symbol_cache_take (symbol_cache, g_steal_pointer (&symbol)); + sysprof_symbol_cache_take (process_info->symbol_cache, g_steal_pointer (&symbol)); } } } @@ -187,19 +180,17 @@ sysprof_document_symbols_worker (GTask *task, { g_autoptr(SysprofDocumentTraceable) traceable = g_list_model_get_item (model, i); int pid = sysprof_document_frame_get_pid (SYSPROF_DOCUMENT_FRAME (traceable)); - SysprofMountNamespace *mount_namespace = g_hash_table_lookup (state->pid_to_mount_namespaces, GINT_TO_POINTER (pid)); - SysprofAddressLayout *address_layout = g_hash_table_lookup (state->pid_to_address_layouts, GINT_TO_POINTER (pid)); - SysprofSymbolCache *symbol_cache = g_hash_table_lookup (state->symbols->pid_to_symbol_cache, GINT_TO_POINTER (pid)); + SysprofProcessInfo *process_info = g_hash_table_lookup (state->pid_to_process_info, GINT_TO_POINTER (pid)); - if (symbol_cache == NULL) - { - symbol_cache = sysprof_symbol_cache_new (); - g_hash_table_insert (state->symbols->pid_to_symbol_cache, - GINT_TO_POINTER (pid), - symbol_cache); - } + /* We might hit this if we have "Process 0" which may be useful to + * let users know can take processing time. For now, that will just + * get skipped unless we deem it really valuable (you'll just jump + * to "- - Kernel - -" anyway. + */ + if (process_info == NULL) + continue; - sysprof_document_symbols_add_traceable (state->symbols, traceable, state->symbolizer, mount_namespace, address_layout, symbol_cache); + sysprof_document_symbols_add_traceable (state->symbols, process_info, traceable, state->symbolizer); } while (gtk_bitset_iter_next (&iter, &i)); } @@ -212,8 +203,7 @@ sysprof_document_symbols_worker (GTask *task, void _sysprof_document_symbols_new (SysprofDocument *document, SysprofSymbolizer *symbolizer, - GHashTable *pid_to_mount_namespaces, - GHashTable *pid_to_address_layouts, + GHashTable *pid_to_process_info, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) @@ -228,8 +218,7 @@ _sysprof_document_symbols_new (SysprofDocument *document, state->document = g_object_ref (document); state->symbolizer = g_object_ref (symbolizer); state->symbols = g_object_new (SYSPROF_TYPE_DOCUMENT_SYMBOLS, NULL); - state->pid_to_address_layouts = g_hash_table_ref (pid_to_address_layouts); - state->pid_to_mount_namespaces = g_hash_table_ref (pid_to_mount_namespaces); + state->pid_to_process_info = g_hash_table_ref (pid_to_process_info); task = g_task_new (NULL, cancellable, callback, user_data); g_task_set_source_tag (task, _sysprof_document_symbols_new); diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index beb8791e..dcf73f2e 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -31,10 +31,12 @@ #include "sysprof-document-file-private.h" #include "sysprof-document-frame-private.h" #include "sysprof-document-mmap.h" +#include "sysprof-document-process-private.h" #include "sysprof-document-symbols-private.h" #include "sysprof-mount-private.h" #include "sysprof-mount-device-private.h" #include "sysprof-mount-namespace-private.h" +#include "sysprof-process-info-private.h" #include "sysprof-strings-private.h" #include "sysprof-symbolizer-private.h" @@ -56,8 +58,7 @@ struct _SysprofDocument GtkBitset *mmaps; GHashTable *files_first_position; - GHashTable *pid_to_address_layouts; - GHashTable *pid_to_mount_namespaces; + GHashTable *pid_to_process_info; SysprofMountNamespace *mount_namespace; @@ -89,16 +90,28 @@ sysprof_document_get_item (GListModel *model, { SysprofDocument *self = SYSPROF_DOCUMENT (model); SysprofDocumentFramePointer *ptr; + SysprofDocumentFrame *ret; if (position >= self->frames->len) return NULL; ptr = &g_array_index (self->frames, SysprofDocumentFramePointer, position); + ret = _sysprof_document_frame_new (self->mapped_file, + (gconstpointer)&self->base[ptr->offset], + ptr->length, + self->needs_swap); - return _sysprof_document_frame_new (self->mapped_file, - (gconstpointer)&self->base[ptr->offset], - ptr->length, - self->needs_swap); + /* Annotate processes with pre-calculated info */ + if (SYSPROF_IS_DOCUMENT_PROCESS (ret)) + { + int pid = sysprof_document_frame_get_pid (ret); + SysprofProcessInfo *process_info = g_hash_table_lookup (self->pid_to_process_info, GINT_TO_POINTER (pid)); + + if (process_info != NULL) + _sysprof_document_process_set_info (SYSPROF_DOCUMENT_PROCESS (ret), process_info); + } + + return ret; } static void @@ -120,6 +133,26 @@ _sysprof_document_traceables (SysprofDocument *self) return self->traceables; } +SysprofProcessInfo * +_sysprof_document_process_info (SysprofDocument *self, + int pid, + gboolean may_create) +{ + SysprofProcessInfo *process_info; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT (self), NULL); + + process_info = g_hash_table_lookup (self->pid_to_process_info, GINT_TO_POINTER (pid)); + + if (process_info == NULL && may_create) + { + process_info = sysprof_process_info_new (sysprof_mount_namespace_copy (self->mount_namespace), pid); + g_hash_table_insert (self->pid_to_process_info, GINT_TO_POINTER (pid), process_info); + } + + return process_info; +} + static void decode_space (gchar **str) { @@ -152,8 +185,7 @@ sysprof_document_finalize (GObject *object) g_clear_pointer (&self->strings, sysprof_strings_unref); - g_clear_pointer (&self->pid_to_address_layouts, g_hash_table_unref); - g_clear_pointer (&self->pid_to_mount_namespaces, g_hash_table_unref); + g_clear_pointer (&self->pid_to_process_info, g_hash_table_unref); g_clear_pointer (&self->mapped_file, g_mapped_file_unref); g_clear_pointer (&self->frames, g_array_unref); @@ -189,8 +221,7 @@ sysprof_document_init (SysprofDocument *self) self->mmaps = gtk_bitset_new_empty (); self->files_first_position = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - self->pid_to_address_layouts = g_hash_table_new_full (NULL, NULL, NULL, g_object_unref); - self->pid_to_mount_namespaces = g_hash_table_new_full (NULL, NULL, NULL, g_object_unref); + self->pid_to_process_info = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)sysprof_process_info_unref); self->mount_namespace = sysprof_mount_namespace_new (); } @@ -209,15 +240,9 @@ sysprof_document_load_memory_maps (SysprofDocument *self) { g_autoptr(SysprofDocumentMmap) map = sysprof_document_get_item ((GListModel *)self, i); int pid = sysprof_document_frame_get_pid (SYSPROF_DOCUMENT_FRAME (map)); - SysprofAddressLayout *address_layout = g_hash_table_lookup (self->pid_to_address_layouts, GINT_TO_POINTER (pid)); + SysprofProcessInfo *process_info = _sysprof_document_process_info (self, pid, TRUE); - if G_UNLIKELY (address_layout == NULL) - { - address_layout = sysprof_address_layout_new (); - g_hash_table_insert (self->pid_to_address_layouts, GINT_TO_POINTER (pid), address_layout); - } - - sysprof_address_layout_take (address_layout, g_steal_pointer (&map)); + sysprof_address_layout_take (process_info->address_layout, g_steal_pointer (&map)); } while (gtk_bitset_iter_next (&iter, &i)); } @@ -304,7 +329,7 @@ sysprof_document_load_mountinfo (SysprofDocument *self, int pid, GBytes *bytes) { - g_autoptr(SysprofMountNamespace) mount_namespace = NULL; + SysprofProcessInfo *process_info; const char *contents; LineReader reader; gsize contents_len; @@ -319,7 +344,10 @@ sysprof_document_load_mountinfo (SysprofDocument *self, g_assert (contents != NULL); g_assert (contents[contents_len] == 0); - mount_namespace = sysprof_mount_namespace_copy (self->mount_namespace); + process_info = _sysprof_document_process_info (self, pid, FALSE); + + g_assert (process_info != NULL); + g_assert (process_info->mount_namespace != NULL); line_reader_init (&reader, (char *)contents, contents_len); while ((line = line_reader_next (&reader, &line_len))) @@ -329,12 +357,8 @@ sysprof_document_load_mountinfo (SysprofDocument *self, line[line_len] = 0; if ((mount = sysprof_mount_new_for_mountinfo (self->strings, line))) - sysprof_mount_namespace_add_mount (mount_namespace, g_steal_pointer (&mount)); + sysprof_mount_namespace_add_mount (process_info->mount_namespace, g_steal_pointer (&mount)); } - - g_hash_table_insert (self->pid_to_mount_namespaces, - GINT_TO_POINTER (pid), - g_steal_pointer (&mount_namespace)); } static void @@ -345,10 +369,9 @@ sysprof_document_load_mountinfos (SysprofDocument *self) g_assert (SYSPROF_IS_DOCUMENT (self)); - g_hash_table_iter_init (&hiter, self->pid_to_address_layouts); + g_hash_table_iter_init (&hiter, self->pid_to_process_info); while (g_hash_table_iter_next (&hiter, &key, &value)) { - g_autoptr(SysprofMountNamespace) mount_namespace = sysprof_mount_namespace_new (); int pid = GPOINTER_TO_INT (key); g_autofree char *path = g_strdup_printf ("/proc/%d/mountinfo", pid); g_autoptr(SysprofDocumentFile) file = sysprof_document_lookup_file (self, path); @@ -565,15 +588,14 @@ sysprof_document_symbolize_prepare_cb (GObject *object, g_assert (self != NULL); g_assert (SYSPROF_IS_DOCUMENT (self)); - g_assert (self->pid_to_mount_namespaces != NULL); + g_assert (self->pid_to_process_info != NULL); if (!_sysprof_symbolizer_prepare_finish (symbolizer, result, &error)) g_task_return_error (task, g_steal_pointer (&error)); else _sysprof_document_symbols_new (g_task_get_source_object (task), symbolizer, - self->pid_to_mount_namespaces, - self->pid_to_address_layouts, + self->pid_to_process_info, g_task_get_cancellable (task), sysprof_document_symbolize_symbols_cb, g_object_ref (task)); diff --git a/src/libsysprof-analyze/sysprof-multi-symbolizer.c b/src/libsysprof-analyze/sysprof-multi-symbolizer.c index b6f39d12..92264c2d 100644 --- a/src/libsysprof-analyze/sysprof-multi-symbolizer.c +++ b/src/libsysprof-analyze/sysprof-multi-symbolizer.c @@ -118,18 +118,16 @@ sysprof_multi_symbolizer_prepare_finish (SysprofSymbolizer *symbolizer, } static SysprofSymbol * -sysprof_multi_symbolizer_symbolize (SysprofSymbolizer *symbolizer, - SysprofMountNamespace *mount_namespace, - SysprofAddressLayout *address_layout, - int pid, - SysprofAddress address) +sysprof_multi_symbolizer_symbolize (SysprofSymbolizer *symbolizer, + const SysprofProcessInfo *process_info, + SysprofAddress address) { SysprofMultiSymbolizer *self = SYSPROF_MULTI_SYMBOLIZER (symbolizer); for (guint i = 0; i < self->symbolizers->len; i++) { SysprofSymbolizer *child = g_ptr_array_index (self->symbolizers, i); - SysprofSymbol *symbol = _sysprof_symbolizer_symbolize (child, mount_namespace, address_layout, pid, address); + SysprofSymbol *symbol = _sysprof_symbolizer_symbolize (child, process_info, address); if (symbol != NULL) return symbol; diff --git a/src/libsysprof-analyze/sysprof-process-info-private.h b/src/libsysprof-analyze/sysprof-process-info-private.h new file mode 100644 index 00000000..2f9cc4a8 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-process-info-private.h @@ -0,0 +1,42 @@ +/* sysprof-process-info-private.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-address-layout-private.h" +#include "sysprof-mount-namespace-private.h" +#include "sysprof-symbol-cache-private.h" + +G_BEGIN_DECLS + +typedef struct _SysprofProcessInfo +{ + SysprofAddressLayout *address_layout; + SysprofMountNamespace *mount_namespace; + SysprofSymbolCache *symbol_cache; + int pid; +} SysprofProcessInfo; + +SysprofProcessInfo *sysprof_process_info_new (SysprofMountNamespace *mount_namespace, + int pid); +SysprofProcessInfo *sysprof_process_info_ref (SysprofProcessInfo *self); +void sysprof_process_info_unref (SysprofProcessInfo *self); + +G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-process-info.c b/src/libsysprof-analyze/sysprof-process-info.c new file mode 100644 index 00000000..5977d5a4 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-process-info.c @@ -0,0 +1,67 @@ +/* sysprof-process-info.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-process-info-private.h" + +G_DEFINE_BOXED_TYPE (SysprofProcessInfo, + sysprof_process_info, + sysprof_process_info_ref, + sysprof_process_info_unref) + +SysprofProcessInfo * +sysprof_process_info_new (SysprofMountNamespace *mount_namespace, + int pid) +{ + SysprofProcessInfo *self; + + self = g_atomic_rc_box_new0 (SysprofProcessInfo); + self->pid = pid; + self->address_layout = sysprof_address_layout_new (); + self->symbol_cache = sysprof_symbol_cache_new (); + self->mount_namespace = mount_namespace; + + return self; +} + +SysprofProcessInfo * +sysprof_process_info_ref (SysprofProcessInfo *self) +{ + return g_atomic_rc_box_acquire (self); +} + +static void +sysprof_process_info_finalize (gpointer data) +{ + SysprofProcessInfo *self = data; + + g_clear_object (&self->address_layout); + g_clear_object (&self->symbol_cache); + g_clear_object (&self->mount_namespace); + + self->pid = 0; +} + +void +sysprof_process_info_unref (SysprofProcessInfo *self) +{ + g_atomic_rc_box_release_full (self, sysprof_process_info_finalize); +} diff --git a/src/libsysprof-analyze/sysprof-symbolizer-private.h b/src/libsysprof-analyze/sysprof-symbolizer-private.h index ad89fa12..abd547ed 100644 --- a/src/libsysprof-analyze/sysprof-symbolizer-private.h +++ b/src/libsysprof-analyze/sysprof-symbolizer-private.h @@ -25,6 +25,7 @@ #include "sysprof-symbol.h" #include "sysprof-symbolizer.h" #include "sysprof-mount-namespace-private.h" +#include "sysprof-process-info-private.h" G_BEGIN_DECLS @@ -39,34 +40,30 @@ struct _SysprofSymbolizerClass { GObjectClass parent_class; - void (*prepare_async) (SysprofSymbolizer *self, - SysprofDocument *document, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); - gboolean (*prepare_finish) (SysprofSymbolizer *self, - GAsyncResult *result, - GError **error); - SysprofSymbol *(*symbolize) (SysprofSymbolizer *self, - SysprofMountNamespace *mount_namespace, - SysprofAddressLayout *address_layout, - int pid, - SysprofAddress address); + void (*prepare_async) (SysprofSymbolizer *self, + SysprofDocument *document, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + gboolean (*prepare_finish) (SysprofSymbolizer *self, + GAsyncResult *result, + GError **error); + SysprofSymbol *(*symbolize) (SysprofSymbolizer *self, + const SysprofProcessInfo *process_info, + SysprofAddress address); }; -void _sysprof_symbolizer_prepare_async (SysprofSymbolizer *self, - SysprofDocument *document, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean _sysprof_symbolizer_prepare_finish (SysprofSymbolizer *self, - GAsyncResult *result, - GError **error); -SysprofSymbol *_sysprof_symbolizer_symbolize (SysprofSymbolizer *self, - SysprofMountNamespace *mount_namespace, - SysprofAddressLayout *address_layout, - int pid, - SysprofAddress address); +void _sysprof_symbolizer_prepare_async (SysprofSymbolizer *self, + SysprofDocument *document, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean _sysprof_symbolizer_prepare_finish (SysprofSymbolizer *self, + GAsyncResult *result, + GError **error); +SysprofSymbol *_sysprof_symbolizer_symbolize (SysprofSymbolizer *self, + const SysprofProcessInfo *process_info, + SysprofAddress address); G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-symbolizer.c b/src/libsysprof-analyze/sysprof-symbolizer.c index f1f148a6..104c81e1 100644 --- a/src/libsysprof-analyze/sysprof-symbolizer.c +++ b/src/libsysprof-analyze/sysprof-symbolizer.c @@ -91,11 +91,9 @@ _sysprof_symbolizer_prepare_finish (SysprofSymbolizer *self, } SysprofSymbol * -_sysprof_symbolizer_symbolize (SysprofSymbolizer *self, - SysprofMountNamespace *mount_namespace, - SysprofAddressLayout *address_layout, - int pid, - SysprofAddress address) +_sysprof_symbolizer_symbolize (SysprofSymbolizer *self, + const SysprofProcessInfo *process_info, + SysprofAddress address) { - return SYSPROF_SYMBOLIZER_GET_CLASS (self)->symbolize (self, mount_namespace, address_layout, pid, address); + return SYSPROF_SYMBOLIZER_GET_CLASS (self)->symbolize (self, process_info, address); } From 5abad47160a3a724588f172f50332a7acaa8ff26 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 11 May 2023 12:32:32 -0700 Subject: [PATCH 0101/1030] libsysprof-analyze: give access to memory maps from process And add it to test tool to ensure it works. --- .../sysprof-document-process.c | 19 +++++++++++++++++++ .../sysprof-document-process.h | 4 ++++ .../tests/test-list-processes.c | 14 ++++++++++++++ 3 files changed, 37 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-document-process.c b/src/libsysprof-analyze/sysprof-document-process.c index 070e49ab..fd89f721 100644 --- a/src/libsysprof-analyze/sysprof-document-process.c +++ b/src/libsysprof-analyze/sysprof-document-process.c @@ -106,6 +106,25 @@ sysprof_document_process_get_command_line (SysprofDocumentProcess *self) return SYSPROF_DOCUMENT_FRAME_CSTRING (self, proc->cmdline); } +/** + * sysprof_document_process_list_memory_maps: + * @self: a #SysprofDocumentProcess + * + * Lists the #SysprofDocumentMmap that are associated with the document. + * + * Returns: (transfer full): a #GListModel of #SysprofDocumentMmap + */ +GListModel * +sysprof_document_process_list_memory_maps (SysprofDocumentProcess *self) +{ + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_PROCESS (self), NULL); + + if (self->process_info == NULL) + return G_LIST_MODEL (g_list_store_new (SYSPROF_TYPE_DOCUMENT_MMAP)); + + return g_object_ref (G_LIST_MODEL (self->process_info->address_layout)); +} + void _sysprof_document_process_set_info (SysprofDocumentProcess *self, SysprofProcessInfo *process_info) diff --git a/src/libsysprof-analyze/sysprof-document-process.h b/src/libsysprof-analyze/sysprof-document-process.h index 5234eb7e..93a795e9 100644 --- a/src/libsysprof-analyze/sysprof-document-process.h +++ b/src/libsysprof-analyze/sysprof-document-process.h @@ -20,6 +20,8 @@ #pragma once +#include + #include "sysprof-document-frame.h" G_BEGIN_DECLS @@ -36,6 +38,8 @@ SYSPROF_AVAILABLE_IN_ALL GType sysprof_document_process_get_type (void) G_GNUC_CONST; SYSPROF_AVAILABLE_IN_ALL const char *sysprof_document_process_get_command_line (SysprofDocumentProcess *self); +SYSPROF_AVAILABLE_IN_ALL +GListModel *sysprof_document_process_list_memory_maps (SysprofDocumentProcess *self); G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofDocumentProcess, g_object_unref) diff --git a/src/libsysprof-analyze/tests/test-list-processes.c b/src/libsysprof-analyze/tests/test-list-processes.c index 4561af33..9c6054e9 100644 --- a/src/libsysprof-analyze/tests/test-list-processes.c +++ b/src/libsysprof-analyze/tests/test-list-processes.c @@ -47,10 +47,24 @@ main (int argc, for (guint i = 0; i < n_items; i++) { g_autoptr(SysprofDocumentProcess) process = g_list_model_get_item (processes, i); + g_autoptr(GListModel) memory_maps = sysprof_document_process_list_memory_maps (process); + guint n_maps; g_print ("%d: %s\n", sysprof_document_frame_get_pid (SYSPROF_DOCUMENT_FRAME (process)), sysprof_document_process_get_command_line (process)); + + n_maps = g_list_model_get_n_items (memory_maps); + + for (guint j = 0; j < n_maps; j++) + { + g_autoptr(SysprofDocumentMmap) map = g_list_model_get_item (memory_maps, j); + + g_print (" [0x%"G_GINT64_MODIFIER"x:0x%"G_GINT64_MODIFIER"x] %s\n", + sysprof_document_mmap_get_start_address (map), + sysprof_document_mmap_get_end_address (map), + sysprof_document_mmap_get_file (map)); + } } return 0; From 5d5f0a5085068e44fd3a71f6dd1284e2d336a0d2 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 11 May 2023 13:09:50 -0700 Subject: [PATCH 0102/1030] libsysprof-analyze: plumb access to string pooling We want various subsystems to start using this, but we need to plumb it to the symbolizers to take advantage of it. --- .../sysprof-bundled-symbolizer.c | 5 +++-- .../sysprof-document-symbols-private.h | 1 + .../sysprof-document-symbols.c | 19 ++++++++++--------- src/libsysprof-analyze/sysprof-document.c | 1 + .../sysprof-multi-symbolizer.c | 3 ++- .../sysprof-symbolizer-private.h | 7 +++++-- src/libsysprof-analyze/sysprof-symbolizer.c | 3 ++- 7 files changed, 24 insertions(+), 15 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-bundled-symbolizer.c b/src/libsysprof-analyze/sysprof-bundled-symbolizer.c index 441f4576..7f3c855b 100644 --- a/src/libsysprof-analyze/sysprof-bundled-symbolizer.c +++ b/src/libsysprof-analyze/sysprof-bundled-symbolizer.c @@ -170,6 +170,7 @@ search_for_symbol_cb (gconstpointer a, static SysprofSymbol * sysprof_bundled_symbolizer_symbolize (SysprofSymbolizer *symbolizer, + SysprofStrings *strings, const SysprofProcessInfo *process_info, SysprofAddress address) { @@ -202,11 +203,11 @@ sysprof_bundled_symbolizer_symbolize (SysprofSymbolizer *symbolizer, if (ret->tag_offset > 0) { if (ret->tag_offset < (self->endptr - self->beginptr)) - tag = g_ref_string_new (&self->beginptr[ret->tag_offset]); + tag = sysprof_strings_get (strings, &self->beginptr[ret->tag_offset]); } if (ret->offset < (self->endptr - self->beginptr)) - return _sysprof_symbol_new (g_ref_string_new (&self->beginptr[ret->offset]), + return _sysprof_symbol_new (sysprof_strings_get (strings, &self->beginptr[ret->offset]), g_steal_pointer (&tag), NULL, ret->addr_begin, diff --git a/src/libsysprof-analyze/sysprof-document-symbols-private.h b/src/libsysprof-analyze/sysprof-document-symbols-private.h index 2dffd414..58ac1faa 100644 --- a/src/libsysprof-analyze/sysprof-document-symbols-private.h +++ b/src/libsysprof-analyze/sysprof-document-symbols-private.h @@ -27,6 +27,7 @@ G_BEGIN_DECLS void _sysprof_document_symbols_new (SysprofDocument *document, + SysprofStrings *strings, SysprofSymbolizer *symbolizer, GHashTable *pid_to_process_info, GCancellable *cancellable, diff --git a/src/libsysprof-analyze/sysprof-document-symbols.c b/src/libsysprof-analyze/sysprof-document-symbols.c index 926d4274..f7c0f796 100644 --- a/src/libsysprof-analyze/sysprof-document-symbols.c +++ b/src/libsysprof-analyze/sysprof-document-symbols.c @@ -66,6 +66,7 @@ typedef struct _Symbolize SysprofDocument *document; SysprofSymbolizer *symbolizer; SysprofDocumentSymbols *symbols; + SysprofStrings *strings; GHashTable *pid_to_process_info; } Symbolize; @@ -75,27 +76,25 @@ symbolize_free (Symbolize *state) g_clear_object (&state->document); g_clear_object (&state->symbolizer); g_clear_object (&state->symbols); + g_clear_pointer (&state->strings, sysprof_strings_unref); g_clear_pointer (&state->pid_to_process_info, g_hash_table_unref); g_free (state); } static void -sysprof_document_symbols_add_traceable (SysprofDocumentSymbols *self, - SysprofProcessInfo *process_info, - SysprofDocumentTraceable *traceable, - SysprofSymbolizer *symbolizer) +add_traceable (SysprofStrings *strings, + SysprofProcessInfo *process_info, + SysprofDocumentTraceable *traceable, + SysprofSymbolizer *symbolizer) { SysprofAddressContext last_context; guint64 *addresses; guint n_addresses; - int pid; - g_assert (SYSPROF_IS_DOCUMENT_SYMBOLS (self)); g_assert (process_info != NULL); g_assert (SYSPROF_IS_DOCUMENT_TRACEABLE (traceable)); g_assert (SYSPROF_IS_SYMBOLIZER (symbolizer)); - pid = sysprof_document_frame_get_pid (SYSPROF_DOCUMENT_FRAME (traceable)); n_addresses = sysprof_document_traceable_get_stack_depth (traceable); addresses = g_alloca (sizeof (guint64) * n_addresses); sysprof_document_traceable_get_stack_addresses (traceable, addresses, n_addresses); @@ -117,7 +116,7 @@ sysprof_document_symbols_add_traceable (SysprofDocumentSymbols *self, } else { - g_autoptr(SysprofSymbol) symbol = _sysprof_symbolizer_symbolize (symbolizer, process_info, address); + g_autoptr(SysprofSymbol) symbol = _sysprof_symbolizer_symbolize (symbolizer, strings, process_info, address); if (symbol != NULL) sysprof_symbol_cache_take (process_info->symbol_cache, g_steal_pointer (&symbol)); @@ -190,7 +189,7 @@ sysprof_document_symbols_worker (GTask *task, if (process_info == NULL) continue; - sysprof_document_symbols_add_traceable (state->symbols, process_info, traceable, state->symbolizer); + add_traceable (state->strings, process_info, traceable, state->symbolizer); } while (gtk_bitset_iter_next (&iter, &i)); } @@ -202,6 +201,7 @@ sysprof_document_symbols_worker (GTask *task, void _sysprof_document_symbols_new (SysprofDocument *document, + SysprofStrings *strings, SysprofSymbolizer *symbolizer, GHashTable *pid_to_process_info, GCancellable *cancellable, @@ -218,6 +218,7 @@ _sysprof_document_symbols_new (SysprofDocument *document, state->document = g_object_ref (document); state->symbolizer = g_object_ref (symbolizer); state->symbols = g_object_new (SYSPROF_TYPE_DOCUMENT_SYMBOLS, NULL); + state->strings = sysprof_strings_ref (strings); state->pid_to_process_info = g_hash_table_ref (pid_to_process_info); task = g_task_new (NULL, cancellable, callback, user_data); diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index dcf73f2e..cb21783e 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -594,6 +594,7 @@ sysprof_document_symbolize_prepare_cb (GObject *object, g_task_return_error (task, g_steal_pointer (&error)); else _sysprof_document_symbols_new (g_task_get_source_object (task), + self->strings, symbolizer, self->pid_to_process_info, g_task_get_cancellable (task), diff --git a/src/libsysprof-analyze/sysprof-multi-symbolizer.c b/src/libsysprof-analyze/sysprof-multi-symbolizer.c index 92264c2d..d39008ea 100644 --- a/src/libsysprof-analyze/sysprof-multi-symbolizer.c +++ b/src/libsysprof-analyze/sysprof-multi-symbolizer.c @@ -119,6 +119,7 @@ sysprof_multi_symbolizer_prepare_finish (SysprofSymbolizer *symbolizer, static SysprofSymbol * sysprof_multi_symbolizer_symbolize (SysprofSymbolizer *symbolizer, + SysprofStrings *strings, const SysprofProcessInfo *process_info, SysprofAddress address) { @@ -127,7 +128,7 @@ sysprof_multi_symbolizer_symbolize (SysprofSymbolizer *symbolizer, for (guint i = 0; i < self->symbolizers->len; i++) { SysprofSymbolizer *child = g_ptr_array_index (self->symbolizers, i); - SysprofSymbol *symbol = _sysprof_symbolizer_symbolize (child, process_info, address); + SysprofSymbol *symbol = _sysprof_symbolizer_symbolize (child, strings, process_info, address); if (symbol != NULL) return symbol; diff --git a/src/libsysprof-analyze/sysprof-symbolizer-private.h b/src/libsysprof-analyze/sysprof-symbolizer-private.h index abd547ed..fadc1f7f 100644 --- a/src/libsysprof-analyze/sysprof-symbolizer-private.h +++ b/src/libsysprof-analyze/sysprof-symbolizer-private.h @@ -22,10 +22,11 @@ #include "sysprof-address-layout-private.h" #include "sysprof-document.h" -#include "sysprof-symbol.h" -#include "sysprof-symbolizer.h" #include "sysprof-mount-namespace-private.h" #include "sysprof-process-info-private.h" +#include "sysprof-strings-private.h" +#include "sysprof-symbol.h" +#include "sysprof-symbolizer.h" G_BEGIN_DECLS @@ -49,6 +50,7 @@ struct _SysprofSymbolizerClass GAsyncResult *result, GError **error); SysprofSymbol *(*symbolize) (SysprofSymbolizer *self, + SysprofStrings *strings, const SysprofProcessInfo *process_info, SysprofAddress address); }; @@ -63,6 +65,7 @@ gboolean _sysprof_symbolizer_prepare_finish (SysprofSymbolizer *se GAsyncResult *result, GError **error); SysprofSymbol *_sysprof_symbolizer_symbolize (SysprofSymbolizer *self, + SysprofStrings *strings, const SysprofProcessInfo *process_info, SysprofAddress address); diff --git a/src/libsysprof-analyze/sysprof-symbolizer.c b/src/libsysprof-analyze/sysprof-symbolizer.c index 104c81e1..14eca803 100644 --- a/src/libsysprof-analyze/sysprof-symbolizer.c +++ b/src/libsysprof-analyze/sysprof-symbolizer.c @@ -92,8 +92,9 @@ _sysprof_symbolizer_prepare_finish (SysprofSymbolizer *self, SysprofSymbol * _sysprof_symbolizer_symbolize (SysprofSymbolizer *self, + SysprofStrings *strings, const SysprofProcessInfo *process_info, SysprofAddress address) { - return SYSPROF_SYMBOLIZER_GET_CLASS (self)->symbolize (self, process_info, address); + return SYSPROF_SYMBOLIZER_GET_CLASS (self)->symbolize (self, strings, process_info, address); } From 9e01216945da5941e58c42807d33507f8c820aef Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 11 May 2023 13:14:19 -0700 Subject: [PATCH 0103/1030] libsysprof-analyze: remove unused code We went a different route for this, so we can just drop the unused code. --- src/libsysprof-analyze/meson.build | 2 - .../sysprof-mapped-file-private.h | 42 ---- src/libsysprof-analyze/sysprof-mapped-file.c | 197 ------------------ .../sysprof-memory-map-private.h | 58 ------ src/libsysprof-analyze/sysprof-memory-map.c | 146 ------------- 5 files changed, 445 deletions(-) delete mode 100644 src/libsysprof-analyze/sysprof-mapped-file-private.h delete mode 100644 src/libsysprof-analyze/sysprof-mapped-file.c delete mode 100644 src/libsysprof-analyze/sysprof-memory-map-private.h delete mode 100644 src/libsysprof-analyze/sysprof-memory-map.c diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build index ae7d037d..707852e9 100644 --- a/src/libsysprof-analyze/meson.build +++ b/src/libsysprof-analyze/meson.build @@ -24,8 +24,6 @@ libsysprof_analyze_public_sources = [ libsysprof_analyze_private_sources = [ 'sysprof-address-layout.c', 'sysprof-document-bitset-index.c', - 'sysprof-mapped-file.c', - 'sysprof-memory-map.c', 'sysprof-mount.c', 'sysprof-mount-device.c', 'sysprof-mount-namespace.c', diff --git a/src/libsysprof-analyze/sysprof-mapped-file-private.h b/src/libsysprof-analyze/sysprof-mapped-file-private.h deleted file mode 100644 index dc666471..00000000 --- a/src/libsysprof-analyze/sysprof-mapped-file-private.h +++ /dev/null @@ -1,42 +0,0 @@ -/* sysprof-mapped-file-private.h - * - * Copyright 2023 Christian Hergert - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -#pragma once - -#include - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_MAPPED_FILE (sysprof_mapped_file_get_type()) - -G_DECLARE_FINAL_TYPE (SysprofMappedFile, sysprof_mapped_file, SYSPROF, MAPPED_FILE, GObject) - -SysprofMappedFile *sysprof_mapped_file_new (char *path, - guint64 address, - guint64 offset, - guint64 length, - gint64 inode); -guint64 sysprof_mapped_file_get_address (SysprofMappedFile *self); -guint64 sysprof_mapped_file_get_offset (SysprofMappedFile *self); -guint64 sysprof_mapped_file_get_length (SysprofMappedFile *self); -gint64 sysprof_mapped_file_get_inode (SysprofMappedFile *self); -const char *sysprof_mapped_file_get_path (SysprofMappedFile *self); - -G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-mapped-file.c b/src/libsysprof-analyze/sysprof-mapped-file.c deleted file mode 100644 index 87c9d177..00000000 --- a/src/libsysprof-analyze/sysprof-mapped-file.c +++ /dev/null @@ -1,197 +0,0 @@ -/* sysprof-mapped-file.c - * - * Copyright 2023 Christian Hergert - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -#include "config.h" - -#include "sysprof-mapped-file-private.h" - -struct _SysprofMappedFile -{ - GObject parent_instance; - char *path; - guint64 address; - guint64 offset; - guint64 length; - gint64 inode; -}; - -enum { - PROP_0, - PROP_ADDRESS, - PROP_INODE, - PROP_LENGTH, - PROP_OFFSET, - PROP_PATH, - N_PROPS -}; - -G_DEFINE_FINAL_TYPE (SysprofMappedFile, sysprof_mapped_file, G_TYPE_OBJECT) - -static GParamSpec *properties [N_PROPS]; - -static void -sysprof_mapped_file_finalize (GObject *object) -{ - SysprofMappedFile *self = (SysprofMappedFile *)object; - - g_clear_pointer (&self->path, g_ref_string_release); - - G_OBJECT_CLASS (sysprof_mapped_file_parent_class)->finalize (object); -} - -static void -sysprof_mapped_file_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - SysprofMappedFile *self = SYSPROF_MAPPED_FILE (object); - - switch (prop_id) - { - case PROP_ADDRESS: - g_value_set_uint64 (value, self->address); - break; - - case PROP_INODE: - g_value_set_int64 (value, self->inode); - break; - - case PROP_LENGTH: - g_value_set_uint64 (value, self->length); - break; - - case PROP_OFFSET: - g_value_set_uint64 (value, self->offset); - break; - - case PROP_PATH: - g_value_set_string (value, self->path); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_mapped_file_class_init (SysprofMappedFileClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->finalize = sysprof_mapped_file_finalize; - object_class->get_property = sysprof_mapped_file_get_property; - - properties [PROP_ADDRESS] = - g_param_spec_uint64 ("address", NULL, NULL, - 0, G_MAXUINT64, 0, - (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - - properties [PROP_INODE] = - g_param_spec_int64 ("inode", NULL, NULL, - G_MININT64, G_MAXINT64, 0, - (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - - properties [PROP_LENGTH] = - g_param_spec_uint64 ("length", NULL, NULL, - 0, G_MAXUINT64, 0, - (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - - properties [PROP_OFFSET] = - g_param_spec_uint64 ("offset", NULL, NULL, - 0, G_MAXUINT64, 0, - (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - - properties [PROP_PATH] = - g_param_spec_string ("path", NULL, NULL, - NULL, - (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_properties (object_class, N_PROPS, properties); -} - -static void -sysprof_mapped_file_init (SysprofMappedFile *self) -{ -} - -/** - * sysprof_mapped_file_new: - * @path: a ref-string for use with g_ref_string_acquire(). - * @address: the address where the file is mapped - * @offset: the offset of the file which is mapped at @address - * @length: the length of the memory mapping in bytes - * @inode: an optional inode of the mapped file, used for validation checks - * - * Creates a new #SysprofMappedFile which represents a mapping within the - * address range of a process. - * - * Returns: (transfer full): a #SysprofMappedFile - */ -SysprofMappedFile * -sysprof_mapped_file_new (char *path, - guint64 address, - guint64 offset, - guint64 length, - gint64 inode) -{ - SysprofMappedFile *self; - - g_return_val_if_fail (path != NULL, NULL); - - self = g_object_new (SYSPROF_TYPE_MAPPED_FILE, NULL); - self->path = g_ref_string_acquire (path); - self->address = address; - self->offset = offset; - self->length = length; - self->inode = inode; - - return self; -} - -guint64 -sysprof_mapped_file_get_address (SysprofMappedFile *self) -{ - return self->address; -} - -guint64 -sysprof_mapped_file_get_offset (SysprofMappedFile *self) -{ - return self->offset; -} - -guint64 -sysprof_mapped_file_get_length (SysprofMappedFile *self) -{ - return self->length; -} - -gint64 -sysprof_mapped_file_get_inode (SysprofMappedFile *self) -{ - return self->inode; -} - -const char * -sysprof_mapped_file_get_path (SysprofMappedFile *self) -{ - return self->path; -} diff --git a/src/libsysprof-analyze/sysprof-memory-map-private.h b/src/libsysprof-analyze/sysprof-memory-map-private.h deleted file mode 100644 index adf740b8..00000000 --- a/src/libsysprof-analyze/sysprof-memory-map-private.h +++ /dev/null @@ -1,58 +0,0 @@ -/* sysprof-memory-map-private.h - * - * Copyright 2023 Christian Hergert - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -#pragma once - -#include - -#include "sysprof-mapped-file-private.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_MEMORY_MAP (sysprof_memory_map_get_type()) - -G_DECLARE_FINAL_TYPE (SysprofMemoryMap, sysprof_memory_map, SYSPROF, MEMORY_MAP, GObject) - -SysprofMemoryMap *sysprof_memory_map_new (GPtrArray *mapped_files); -SysprofMappedFile *sysprof_memory_map_find_at_address (SysprofMemoryMap *self, - guint64 address); - -typedef GPtrArray* SysprofMemoryMapBuilder; - -static inline void -sysprof_memory_map_builder_init (SysprofMemoryMapBuilder *builder) -{ - *builder = g_ptr_array_new_with_free_func (g_object_unref); -} - -static inline void -sysprof_memory_map_builder_add (SysprofMemoryMapBuilder *builder, - SysprofMappedFile *mapped_file) -{ - g_ptr_array_add (*builder, mapped_file); -} - -static inline SysprofMemoryMap * -sysprof_memory_map_builder_end (SysprofMemoryMapBuilder *builder) -{ - return sysprof_memory_map_new (g_steal_pointer (builder)); -} - -G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-memory-map.c b/src/libsysprof-analyze/sysprof-memory-map.c deleted file mode 100644 index 6c265416..00000000 --- a/src/libsysprof-analyze/sysprof-memory-map.c +++ /dev/null @@ -1,146 +0,0 @@ -/* sysprof-memory-map.c - * - * Copyright 2023 Christian Hergert - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -#include "config.h" - -#include - -#include "sysprof-memory-map-private.h" - -struct _SysprofMemoryMap -{ - GObject parent_instance; - GPtrArray *mapped_files; -}; - -G_DEFINE_FINAL_TYPE (SysprofMemoryMap, sysprof_memory_map, G_TYPE_OBJECT) - -static void -sysprof_memory_map_finalize (GObject *object) -{ - SysprofMemoryMap *self = (SysprofMemoryMap *)object; - - g_clear_pointer (&self->mapped_files, g_ptr_array_unref); - - G_OBJECT_CLASS (sysprof_memory_map_parent_class)->finalize (object); -} - -static void -sysprof_memory_map_class_init (SysprofMemoryMapClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->finalize = sysprof_memory_map_finalize; -} - -static void -sysprof_memory_map_init (SysprofMemoryMap *self) -{ - self->mapped_files = g_ptr_array_new_with_free_func (g_object_unref); -} - -static int -mapped_file_compare (gconstpointer aptr, - gconstpointer bptr) -{ - SysprofMappedFile * const *a = aptr; - SysprofMappedFile * const *b = bptr; - guint64 aaddr = sysprof_mapped_file_get_address (*a); - guint64 baddr = sysprof_mapped_file_get_address (*b); - - if (aaddr < baddr) - return -1; - else if (aaddr > baddr) - return 1; - else - return 0; -} - -static int -mapped_file_bsearch (gconstpointer keyptr, - gconstpointer elemptr) -{ - guint64 address = *(const guint64 *)keyptr; - SysprofMappedFile *mapped_file = *(SysprofMappedFile * const *)elemptr; - guint64 begin = sysprof_mapped_file_get_address (mapped_file); - guint64 length = sysprof_mapped_file_get_length (mapped_file); - - if (address < begin) - return -1; - - if (address >= begin + length) - return 1; - - return 0; -} - -/** - * sysprof_memory_map_new: - * @mapped_files: (transfer full): a #GPtrArray of #SysprofMappedFile - * - * Creates a new #SysprofMemoryMap that can be used to locate a mapped - * file which contains a particular address within a target process. - * - * You should use SysprofMemoryMapBuilder instead of this function. - * - * Returns: (transfer full): a #SysprofMemoryMap - */ -SysprofMemoryMap * -sysprof_memory_map_new (GPtrArray *mapped_files) -{ - SysprofMemoryMap *self; - - g_return_val_if_fail (mapped_files != NULL, NULL); - - g_ptr_array_set_free_func (mapped_files, g_object_unref); - g_ptr_array_sort (mapped_files, mapped_file_compare); - - self = g_object_new (SYSPROF_TYPE_MEMORY_MAP, NULL); - self->mapped_files = mapped_files; - - return self; -} - -/** - * sysprof_memory_map_find_at_address: - * @self: a #SysprofMemoryMap - * @address: an address - * - * Attempts to locate the #SysprofMappedFile which contains @address. - * - * Returns: (transfer none) (nullable): a #SysprofMappedFile if there - * was a mapping containing @address, otherwise %NULL. - */ -SysprofMappedFile * -sysprof_memory_map_find_at_address (SysprofMemoryMap *self, - guint64 address) -{ - SysprofMappedFile **match; - - g_return_val_if_fail (SYSPROF_IS_MEMORY_MAP (self), NULL); - - match = bsearch (&address, - (gpointer)self->mapped_files->pdata, - self->mapped_files->len, - sizeof (gpointer), - mapped_file_bsearch); - - return match ? *match : NULL; -} From f914580675de1dafaa0e64776c6b9025cc53f53e Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 11 May 2023 13:15:28 -0700 Subject: [PATCH 0104/1030] libsysprof-analyze: remove process list This is done with a GtkBitset index and so we can drop this unused list model wrapper. --- src/libsysprof-analyze/meson.build | 2 - src/libsysprof-analyze/sysprof-analyze.h | 1 - .../sysprof-document-process-list.c | 170 ------------------ .../sysprof-document-process-list.h | 42 ----- 4 files changed, 215 deletions(-) delete mode 100644 src/libsysprof-analyze/sysprof-document-process-list.c delete mode 100644 src/libsysprof-analyze/sysprof-document-process-list.h diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build index 707852e9..7d3851da 100644 --- a/src/libsysprof-analyze/meson.build +++ b/src/libsysprof-analyze/meson.build @@ -12,7 +12,6 @@ libsysprof_analyze_public_sources = [ 'sysprof-document-metadata.c', 'sysprof-document-mmap.c', 'sysprof-document-process.c', - 'sysprof-document-process-list.c', 'sysprof-document-sample.c', 'sysprof-document-symbols.c', 'sysprof-document-traceable.c', @@ -47,7 +46,6 @@ libsysprof_analyze_public_headers = [ 'sysprof-document-metadata.h', 'sysprof-document-mmap.h', 'sysprof-document-process.h', - 'sysprof-document-process-list.h', 'sysprof-document-sample.h', 'sysprof-document-symbols.h', 'sysprof-document-traceable.h', diff --git a/src/libsysprof-analyze/sysprof-analyze.h b/src/libsysprof-analyze/sysprof-analyze.h index f81e185d..ad4edf25 100644 --- a/src/libsysprof-analyze/sysprof-analyze.h +++ b/src/libsysprof-analyze/sysprof-analyze.h @@ -38,7 +38,6 @@ G_BEGIN_DECLS # include "sysprof-document-metadata.h" # include "sysprof-document-mmap.h" # include "sysprof-document-process.h" -# include "sysprof-document-process-list.h" # include "sysprof-document-sample.h" # include "sysprof-document-symbols.h" # include "sysprof-document-traceable.h" diff --git a/src/libsysprof-analyze/sysprof-document-process-list.c b/src/libsysprof-analyze/sysprof-document-process-list.c deleted file mode 100644 index e43f8284..00000000 --- a/src/libsysprof-analyze/sysprof-document-process-list.c +++ /dev/null @@ -1,170 +0,0 @@ -/* - * sysprof-document-process-list.c - * - * Copyright 2023 Christian Hergert - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -#include "config.h" - -#include "sysprof-document-process-list.h" - -struct _SysprofDocumentProcessList -{ - GObject parent_instance; - GListModel *model; -}; - -static void list_model_iface_init (GListModelInterface *iface); - -G_DEFINE_FINAL_TYPE_WITH_CODE (SysprofDocumentProcessList, sysprof_document_process_list, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init)) - -enum { - PROP_0, - PROP_MODEL, - N_PROPS -}; - -static GParamSpec *properties [N_PROPS]; - -/** - * sysprof_document_process_list_new: - * @model: a #GListModel or %NULL - * - * Creates a new #SysprofDocumentProcessList. - * - * The resulting process list can be used to determine the lifetime of - * processes that were found within the document. - * - * Returns: (transfer full): a new #SysprofDocumentProcessList - */ -SysprofDocumentProcessList * -sysprof_document_process_list_new (GListModel *model) -{ - g_return_val_if_fail (!model || G_IS_LIST_MODEL (model), NULL); - - return g_object_new (SYSPROF_TYPE_DOCUMENT_PROCESS_LIST, - "model", model, - NULL); -} - -static void -sysprof_document_process_list_finalize (GObject *object) -{ - SysprofDocumentProcessList *self = (SysprofDocumentProcessList *)object; - - g_clear_object (&self->model); - - G_OBJECT_CLASS (sysprof_document_process_list_parent_class)->finalize (object); -} - -static void -sysprof_document_process_list_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - SysprofDocumentProcessList *self = SYSPROF_DOCUMENT_PROCESS_LIST (object); - - switch (prop_id) - { - case PROP_MODEL: - g_value_set_object (value, sysprof_document_process_list_get_model (self)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_document_process_list_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - SysprofDocumentProcessList *self = SYSPROF_DOCUMENT_PROCESS_LIST (object); - - switch (prop_id) - { - case PROP_MODEL: - sysprof_document_process_list_set_model (self, g_value_get_object (value)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_document_process_list_class_init (SysprofDocumentProcessListClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->finalize = sysprof_document_process_list_finalize; - object_class->get_property = sysprof_document_process_list_get_property; - object_class->set_property = sysprof_document_process_list_set_property; - - properties [PROP_MODEL] = - g_param_spec_object ("model", NULL, NULL, - G_TYPE_OBJECT, - (G_PARAM_READWRITE | - G_PARAM_EXPLICIT_NOTIFY | - G_PARAM_STATIC_STRINGS)); - - g_object_class_install_properties (object_class, N_PROPS, properties); -} - -static void -sysprof_document_process_list_init (SysprofDocumentProcessList *self) -{ -} - -static void -list_model_iface_init (GListModelInterface *iface) -{ -} - -/** - * sysprof_document_process_list_get_model: - * @self: a #SysprofDocumentProcessList - * - * Gets the underlying model containing processes to extract. - * - * Returns: (transfer none) (nullable): a #GListModel or %NULL - */ -GListModel * -sysprof_document_process_list_get_model (SysprofDocumentProcessList *self) -{ - g_return_val_if_fail (SYSPROF_IS_DOCUMENT_PROCESS_LIST (self), NULL); - - return self->model; -} - -void -sysprof_document_process_list_set_model (SysprofDocumentProcessList *self, - GListModel *model) -{ - g_return_if_fail (SYSPROF_IS_DOCUMENT_PROCESS_LIST (self)); - g_return_if_fail (!model || G_IS_LIST_MODEL (model)); - - if (g_set_object (&self->model, model)) - { - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_MODEL]); - } -} diff --git a/src/libsysprof-analyze/sysprof-document-process-list.h b/src/libsysprof-analyze/sysprof-document-process-list.h deleted file mode 100644 index 72309bae..00000000 --- a/src/libsysprof-analyze/sysprof-document-process-list.h +++ /dev/null @@ -1,42 +0,0 @@ -/* sysprof-document-process-list.h - * - * Copyright 2023 Christian Hergert - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -#pragma once - -#include - -#include - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_DOCUMENT_PROCESS_LIST (sysprof_document_process_list_get_type()) - -SYSPROF_AVAILABLE_IN_ALL -G_DECLARE_FINAL_TYPE (SysprofDocumentProcessList, sysprof_document_process_list, SYSPROF, DOCUMENT_PROCESS_LIST, GObject) - -SYSPROF_AVAILABLE_IN_ALL -SysprofDocumentProcessList *sysprof_document_process_list_new (GListModel *model); -SYSPROF_AVAILABLE_IN_ALL -GListModel *sysprof_document_process_list_get_model (SysprofDocumentProcessList *self); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_document_process_list_set_model (SysprofDocumentProcessList *self, - GListModel *model); - -G_END_DECLS From a393dd9acdb48f1c0d5c0eab05acc39965d04814 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 11 May 2023 13:58:34 -0700 Subject: [PATCH 0105/1030] libsysprof-analyze: parse various data from mountinfo We still need to parse optional fields for filesystem type and what not so that we can resolve btrfs subvolumes and more. --- .../sysprof-mount-private.h | 4 +- src/libsysprof-analyze/sysprof-mount.c | 76 ++++++++++++++++--- 2 files changed, 69 insertions(+), 11 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-mount-private.h b/src/libsysprof-analyze/sysprof-mount-private.h index 675972d6..da08422f 100644 --- a/src/libsysprof-analyze/sysprof-mount-private.h +++ b/src/libsysprof-analyze/sysprof-mount-private.h @@ -34,8 +34,10 @@ SysprofMount *sysprof_mount_new_for_mountinfo (SysprofStrings *strings, const char *mountinfo); int sysprof_mount_get_device_major (SysprofMount *self); int sysprof_mount_get_device_minor (SysprofMount *self); +int sysprof_mount_get_mount_id (SysprofMount *self); +int sysprof_mount_get_parent_mount_id (SysprofMount *self); const char *sysprof_mount_get_root (SysprofMount *self); -const char *sysprof_mount_get_mount_path (SysprofMount *self); +const char *sysprof_mount_get_mount_point (SysprofMount *self); const char *sysprof_mount_get_mount_source (SysprofMount *self); const char *sysprof_mount_get_filesystem_type (SysprofMount *self); const char *sysprof_mount_get_superblock_option (SysprofMount *self, diff --git a/src/libsysprof-analyze/sysprof-mount.c b/src/libsysprof-analyze/sysprof-mount.c index 4d3bdf19..ec42dab9 100644 --- a/src/libsysprof-analyze/sysprof-mount.c +++ b/src/libsysprof-analyze/sysprof-mount.c @@ -20,15 +20,19 @@ #include "config.h" +#include + #include "sysprof-mount-private.h" struct _SysprofMount { GObject parent_instance; + int mount_id; + int parent_mount_id; int device_major; int device_minor; GRefString *root; - GRefString *mount_path; + GRefString *mount_point; GRefString *mount_source; GRefString *filesystem_type; GRefString *superblock_options; @@ -39,8 +43,10 @@ enum { PROP_DEVICE_MAJOR, PROP_DEVICE_MINOR, PROP_ROOT, - PROP_MOUNT_PATH, + PROP_MOUNT_ID, + PROP_MOUNT_POINT, PROP_MOUNT_SOURCE, + PROP_PARENT_MOUNT_ID, PROP_FILESYSTEM_TYPE, N_PROPS }; @@ -55,7 +61,7 @@ sysprof_mount_finalize (GObject *object) SysprofMount *self = (SysprofMount *)object; g_clear_pointer (&self->root, g_ref_string_release); - g_clear_pointer (&self->mount_path, g_ref_string_release); + g_clear_pointer (&self->mount_point, g_ref_string_release); g_clear_pointer (&self->mount_source, g_ref_string_release); g_clear_pointer (&self->filesystem_type, g_ref_string_release); g_clear_pointer (&self->superblock_options, g_ref_string_release); @@ -85,14 +91,22 @@ sysprof_mount_get_property (GObject *object, g_value_set_string (value, sysprof_mount_get_root (self)); break; - case PROP_MOUNT_PATH: - g_value_set_string (value, sysprof_mount_get_mount_path (self)); + case PROP_MOUNT_ID: + g_value_set_int (value, sysprof_mount_get_mount_id (self)); + break; + + case PROP_MOUNT_POINT: + g_value_set_string (value, sysprof_mount_get_mount_point (self)); break; case PROP_MOUNT_SOURCE: g_value_set_string (value, sysprof_mount_get_mount_source (self)); break; + case PROP_PARENT_MOUNT_ID: + g_value_set_int (value, sysprof_mount_get_parent_mount_id (self)); + break; + case PROP_FILESYSTEM_TYPE: g_value_set_string (value, sysprof_mount_get_filesystem_type (self)); break; @@ -125,8 +139,18 @@ sysprof_mount_class_init (SysprofMountClass *klass) NULL, (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - properties[PROP_MOUNT_PATH] = - g_param_spec_string ("mount-path", NULL, NULL, + properties[PROP_MOUNT_ID] = + g_param_spec_int ("mount-id", NULL, NULL, + G_MININT, G_MAXINT, 0, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + properties[PROP_PARENT_MOUNT_ID] = + g_param_spec_int ("parent-mount-id", NULL, NULL, + G_MININT, G_MAXINT, 0, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + properties[PROP_MOUNT_POINT] = + g_param_spec_string ("mount-point", NULL, NULL, NULL, (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); @@ -152,7 +176,27 @@ SysprofMount * sysprof_mount_new_for_mountinfo (SysprofStrings *strings, const char *mountinfo) { - return NULL; + g_autoptr(SysprofMount) self = NULL; + g_auto(GStrv) parts = NULL; + gsize n_parts; + + g_return_val_if_fail (strings != NULL, NULL); + g_return_val_if_fail (mountinfo != NULL, NULL); + + parts = g_strsplit (mountinfo, " ", 20); + n_parts = g_strv_length (parts); + if (n_parts < 10) + return NULL; + + self = g_object_new (SYSPROF_TYPE_MOUNT, NULL); + + self->mount_id = g_ascii_strtoll (parts[0], NULL, 10); + self->parent_mount_id = g_ascii_strtoll (parts[1], NULL, 10); + sscanf (parts[2], "%d:%d", &self->device_major, &self->device_minor); + self->root = sysprof_strings_get (strings, parts[3]); + self->mount_point = sysprof_strings_get (strings, parts[4]); + + return g_steal_pointer (&self); } int @@ -174,9 +218,9 @@ sysprof_mount_get_root (SysprofMount *self) } const char * -sysprof_mount_get_mount_path (SysprofMount *self) +sysprof_mount_get_mount_point (SysprofMount *self) { - return self->mount_path; + return self->mount_point; } const char * @@ -197,3 +241,15 @@ sysprof_mount_get_superblock_option (SysprofMount *self, { return NULL; } + +int +sysprof_mount_get_mount_id (SysprofMount *self) +{ + return self->mount_id; +} + +int +sysprof_mount_get_parent_mount_id (SysprofMount *self) +{ + return self->parent_mount_id; +} From 352fa617c0a8bada72d2c816a1f5aa7e7fa61d44 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 11 May 2023 14:05:07 -0700 Subject: [PATCH 0106/1030] libsysprof-analyze: fix transfer ownership of refstring --- src/libsysprof-analyze/sysprof-document-symbols.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-document-symbols.c b/src/libsysprof-analyze/sysprof-document-symbols.c index f7c0f796..2f06a79d 100644 --- a/src/libsysprof-analyze/sysprof-document-symbols.c +++ b/src/libsysprof-analyze/sysprof-document-symbols.c @@ -161,8 +161,7 @@ sysprof_document_symbols_worker (GTask *task, /* Create static symbols for context switch use */ for (guint cs = 0; cs < G_N_ELEMENTS (context_switches); cs++) { - g_autoptr(GRefString) name = g_ref_string_new_intern (context_switches[cs].name); - g_autoptr(SysprofSymbol) symbol = _sysprof_symbol_new (name, NULL, NULL, 0, 0); + g_autoptr(SysprofSymbol) symbol = _sysprof_symbol_new (g_ref_string_new_intern (context_switches[cs].name), NULL, NULL, 0, 0); /* TODO: It would be nice if we had enough insight from the capture header * as to the host system, so we can show "vmlinuz" and "Linux" respectively From 7c37120edf6b2c8ab34a76b8dd2a651bac722eb6 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 11 May 2023 14:37:02 -0700 Subject: [PATCH 0107/1030] libsysprof-analyze: make SysprofMount public API And expose it via sysprof_document_process_list_mounts() so that when inspecting processes we can see what binaries were mapped as well as what the filesystem looked like to locate those mapped paths. --- src/libsysprof-analyze/meson.build | 1 + src/libsysprof-analyze/sysprof-analyze.h | 1 + .../sysprof-document-process.c | 22 +++++++- .../sysprof-document-process.h | 2 + .../sysprof-mount-namespace.c | 38 ++++++++++++- .../sysprof-mount-private.h | 21 ++------ src/libsysprof-analyze/sysprof-mount.h | 54 +++++++++++++++++++ .../tests/test-list-processes.c | 21 +++++++- 8 files changed, 138 insertions(+), 22 deletions(-) create mode 100644 src/libsysprof-analyze/sysprof-mount.h diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build index 7d3851da..f242601d 100644 --- a/src/libsysprof-analyze/meson.build +++ b/src/libsysprof-analyze/meson.build @@ -49,6 +49,7 @@ libsysprof_analyze_public_headers = [ 'sysprof-document-sample.h', 'sysprof-document-symbols.h', 'sysprof-document-traceable.h', + 'sysprof-mount.h', 'sysprof-multi-symbolizer.h', 'sysprof-symbol.h', 'sysprof-symbolizer.h', diff --git a/src/libsysprof-analyze/sysprof-analyze.h b/src/libsysprof-analyze/sysprof-analyze.h index ad4edf25..b47f3d54 100644 --- a/src/libsysprof-analyze/sysprof-analyze.h +++ b/src/libsysprof-analyze/sysprof-analyze.h @@ -41,6 +41,7 @@ G_BEGIN_DECLS # include "sysprof-document-sample.h" # include "sysprof-document-symbols.h" # include "sysprof-document-traceable.h" +# include "sysprof-mount.h" # include "sysprof-multi-symbolizer.h" # include "sysprof-symbol.h" # include "sysprof-symbolizer.h" diff --git a/src/libsysprof-analyze/sysprof-document-process.c b/src/libsysprof-analyze/sysprof-document-process.c index fd89f721..48bbca32 100644 --- a/src/libsysprof-analyze/sysprof-document-process.c +++ b/src/libsysprof-analyze/sysprof-document-process.c @@ -22,6 +22,7 @@ #include "sysprof-document-frame-private.h" #include "sysprof-document-process-private.h" +#include "sysprof-mount.h" struct _SysprofDocumentProcess { @@ -110,7 +111,7 @@ sysprof_document_process_get_command_line (SysprofDocumentProcess *self) * sysprof_document_process_list_memory_maps: * @self: a #SysprofDocumentProcess * - * Lists the #SysprofDocumentMmap that are associated with the document. + * Lists the #SysprofDocumentMmap that are associated with the process. * * Returns: (transfer full): a #GListModel of #SysprofDocumentMmap */ @@ -125,6 +126,25 @@ sysprof_document_process_list_memory_maps (SysprofDocumentProcess *self) return g_object_ref (G_LIST_MODEL (self->process_info->address_layout)); } +/** + * sysprof_document_process_list_mounts: + * @self: a #SysprofDocumentProcess + * + * Lists the #SysprofMount that are associated with the process. + * + * Returns: (transfer full): a #GListModel of #SysprofMount + */ +GListModel * +sysprof_document_process_list_mounts (SysprofDocumentProcess *self) +{ + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_PROCESS (self), NULL); + + if (self->process_info == NULL) + return G_LIST_MODEL (g_list_store_new (SYSPROF_TYPE_MOUNT)); + + return g_object_ref (G_LIST_MODEL (self->process_info->mount_namespace)); +} + void _sysprof_document_process_set_info (SysprofDocumentProcess *self, SysprofProcessInfo *process_info) diff --git a/src/libsysprof-analyze/sysprof-document-process.h b/src/libsysprof-analyze/sysprof-document-process.h index 93a795e9..83ce34fa 100644 --- a/src/libsysprof-analyze/sysprof-document-process.h +++ b/src/libsysprof-analyze/sysprof-document-process.h @@ -40,6 +40,8 @@ SYSPROF_AVAILABLE_IN_ALL const char *sysprof_document_process_get_command_line (SysprofDocumentProcess *self); SYSPROF_AVAILABLE_IN_ALL GListModel *sysprof_document_process_list_memory_maps (SysprofDocumentProcess *self); +SYSPROF_AVAILABLE_IN_ALL +GListModel *sysprof_document_process_list_mounts (SysprofDocumentProcess *self); G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofDocumentProcess, g_object_unref) diff --git a/src/libsysprof-analyze/sysprof-mount-namespace.c b/src/libsysprof-analyze/sysprof-mount-namespace.c index d09e33d6..c096aebf 100644 --- a/src/libsysprof-analyze/sysprof-mount-namespace.c +++ b/src/libsysprof-analyze/sysprof-mount-namespace.c @@ -20,6 +20,8 @@ #include "config.h" +#include + #include "sysprof-mount-namespace-private.h" struct _SysprofMountNamespace @@ -29,7 +31,40 @@ struct _SysprofMountNamespace GPtrArray *mounts; }; -G_DEFINE_FINAL_TYPE (SysprofMountNamespace, sysprof_mount_namespace, G_TYPE_OBJECT) +static GType +sysprof_mount_namespace_get_item_type (GListModel *model) +{ + return SYSPROF_TYPE_MOUNT; +} + +static guint +sysprof_mount_namespace_get_n_items (GListModel *model) +{ + return SYSPROF_MOUNT_NAMESPACE (model)->mounts->len; +} + +static gpointer +sysprof_mount_namespace_get_item (GListModel *model, + guint position) +{ + SysprofMountNamespace *self = SYSPROF_MOUNT_NAMESPACE (model); + + if (position < self->mounts->len) + return g_object_ref (g_ptr_array_index (self->mounts, position)); + + return NULL; +} + +static void +list_model_iface_init (GListModelInterface *iface) +{ + iface->get_item_type = sysprof_mount_namespace_get_item_type; + iface->get_item = sysprof_mount_namespace_get_item; + iface->get_n_items = sysprof_mount_namespace_get_n_items; +} + +G_DEFINE_FINAL_TYPE_WITH_CODE (SysprofMountNamespace, sysprof_mount_namespace, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init)) static void sysprof_mount_namespace_finalize (GObject *object) @@ -54,6 +89,7 @@ static void sysprof_mount_namespace_init (SysprofMountNamespace *self) { self->devices = g_ptr_array_new_with_free_func (g_object_unref); + self->mounts = g_ptr_array_new_with_free_func (g_object_unref); } SysprofMountNamespace * diff --git a/src/libsysprof-analyze/sysprof-mount-private.h b/src/libsysprof-analyze/sysprof-mount-private.h index da08422f..37bf86cb 100644 --- a/src/libsysprof-analyze/sysprof-mount-private.h +++ b/src/libsysprof-analyze/sysprof-mount-private.h @@ -20,27 +20,12 @@ #pragma once -#include - +#include "sysprof-mount.h" #include "sysprof-strings-private.h" G_BEGIN_DECLS -#define SYSPROF_TYPE_MOUNT (sysprof_mount_get_type()) - -G_DECLARE_FINAL_TYPE (SysprofMount, sysprof_mount, SYSPROF, MOUNT, GObject) - -SysprofMount *sysprof_mount_new_for_mountinfo (SysprofStrings *strings, - const char *mountinfo); -int sysprof_mount_get_device_major (SysprofMount *self); -int sysprof_mount_get_device_minor (SysprofMount *self); -int sysprof_mount_get_mount_id (SysprofMount *self); -int sysprof_mount_get_parent_mount_id (SysprofMount *self); -const char *sysprof_mount_get_root (SysprofMount *self); -const char *sysprof_mount_get_mount_point (SysprofMount *self); -const char *sysprof_mount_get_mount_source (SysprofMount *self); -const char *sysprof_mount_get_filesystem_type (SysprofMount *self); -const char *sysprof_mount_get_superblock_option (SysprofMount *self, - const char *option); +SysprofMount *sysprof_mount_new_for_mountinfo (SysprofStrings *strings, + const char *mountinfo); G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-mount.h b/src/libsysprof-analyze/sysprof-mount.h new file mode 100644 index 00000000..c7ee83ff --- /dev/null +++ b/src/libsysprof-analyze/sysprof-mount.h @@ -0,0 +1,54 @@ +/* sysprof-mount.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +#include + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_MOUNT (sysprof_mount_get_type()) + +SYSPROF_AVAILABLE_IN_ALL +G_DECLARE_FINAL_TYPE (SysprofMount, sysprof_mount, SYSPROF, MOUNT, GObject) + +SYSPROF_AVAILABLE_IN_ALL +int sysprof_mount_get_device_major (SysprofMount *self); +SYSPROF_AVAILABLE_IN_ALL +int sysprof_mount_get_device_minor (SysprofMount *self); +SYSPROF_AVAILABLE_IN_ALL +int sysprof_mount_get_mount_id (SysprofMount *self); +SYSPROF_AVAILABLE_IN_ALL +int sysprof_mount_get_parent_mount_id (SysprofMount *self); +SYSPROF_AVAILABLE_IN_ALL +const char *sysprof_mount_get_root (SysprofMount *self); +SYSPROF_AVAILABLE_IN_ALL +const char *sysprof_mount_get_mount_point (SysprofMount *self); +SYSPROF_AVAILABLE_IN_ALL +const char *sysprof_mount_get_mount_source (SysprofMount *self); +SYSPROF_AVAILABLE_IN_ALL +const char *sysprof_mount_get_filesystem_type (SysprofMount *self); +SYSPROF_AVAILABLE_IN_ALL +const char *sysprof_mount_get_superblock_option (SysprofMount *self, + const char *option); + +G_END_DECLS diff --git a/src/libsysprof-analyze/tests/test-list-processes.c b/src/libsysprof-analyze/tests/test-list-processes.c index 9c6054e9..2006bf32 100644 --- a/src/libsysprof-analyze/tests/test-list-processes.c +++ b/src/libsysprof-analyze/tests/test-list-processes.c @@ -48,23 +48,40 @@ main (int argc, { g_autoptr(SysprofDocumentProcess) process = g_list_model_get_item (processes, i); g_autoptr(GListModel) memory_maps = sysprof_document_process_list_memory_maps (process); + g_autoptr(GListModel) mounts = sysprof_document_process_list_mounts (process); guint n_maps; + guint n_mounts; g_print ("%d: %s\n", sysprof_document_frame_get_pid (SYSPROF_DOCUMENT_FRAME (process)), sysprof_document_process_get_command_line (process)); + g_print (" Address Layout:\n"); n_maps = g_list_model_get_n_items (memory_maps); - for (guint j = 0; j < n_maps; j++) { g_autoptr(SysprofDocumentMmap) map = g_list_model_get_item (memory_maps, j); - g_print (" [0x%"G_GINT64_MODIFIER"x:0x%"G_GINT64_MODIFIER"x] %s\n", + g_print (" [0x%"G_GINT64_MODIFIER"x:0x%"G_GINT64_MODIFIER"x] %s\n", sysprof_document_mmap_get_start_address (map), sysprof_document_mmap_get_end_address (map), sysprof_document_mmap_get_file (map)); } + + g_print (" Mounts:\n"); + n_mounts = g_list_model_get_n_items (mounts); + for (guint j = 0; j < n_mounts; j++) + { + g_autoptr(SysprofMount) mount = g_list_model_get_item (mounts, j); + + g_print (" %d %d %d:%d %s %s\n", + sysprof_mount_get_mount_id (mount), + sysprof_mount_get_parent_mount_id (mount), + sysprof_mount_get_device_major (mount), + sysprof_mount_get_device_minor (mount), + sysprof_mount_get_root (mount), + sysprof_mount_get_mount_point (mount)); + } } return 0; From 971166a82f403c6ad5d16ff4ce31ebb4151435fe Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 11 May 2023 15:02:32 -0700 Subject: [PATCH 0108/1030] libsysprof-analyze: keep bitset of known pids This can be handy for iterating all the pids we know we've seen during process loading. --- src/libsysprof-analyze/sysprof-document.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index cb21783e..e5a9989d 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -56,6 +56,7 @@ struct _SysprofDocument GtkBitset *traceables; GtkBitset *processes; GtkBitset *mmaps; + GtkBitset *pids; GHashTable *files_first_position; GHashTable *pid_to_process_info; @@ -193,6 +194,7 @@ sysprof_document_finalize (GObject *object) g_clear_pointer (&self->processes, gtk_bitset_unref); g_clear_pointer (&self->mmaps, gtk_bitset_unref); g_clear_pointer (&self->file_chunks, gtk_bitset_unref); + g_clear_pointer (&self->pids, gtk_bitset_unref); g_clear_object (&self->mount_namespace); @@ -219,6 +221,7 @@ sysprof_document_init (SysprofDocument *self) self->file_chunks = gtk_bitset_new_empty (); self->processes = gtk_bitset_new_empty (); self->mmaps = gtk_bitset_new_empty (); + self->pids = gtk_bitset_new_empty (); self->files_first_position = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); self->pid_to_process_info = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)sysprof_process_info_unref); @@ -425,6 +428,7 @@ sysprof_document_load (SysprofDocument *self, const SysprofCaptureFrame *tainted; SysprofDocumentFramePointer ptr; guint16 frame_len; + int pid; memcpy (&frame_len, &self->base[pos], sizeof frame_len); if (self->needs_swap) @@ -438,6 +442,10 @@ sysprof_document_load (SysprofDocument *self, tainted = (const SysprofCaptureFrame *)(gpointer)&self->base[pos]; + pid = self->needs_swap ? GUINT32_SWAP_LE_BE (tainted->pid) : tainted->pid; + + gtk_bitset_add (self->pids, pid); + if (tainted->type == SYSPROF_CAPTURE_FRAME_SAMPLE || tainted->type == SYSPROF_CAPTURE_FRAME_ALLOCATION) gtk_bitset_add (self->traceables, self->frames->len); From 1f9d37837d310f17ff2287e7be0025e1d33c4765 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 11 May 2023 15:02:52 -0700 Subject: [PATCH 0109/1030] libsysprof-analyze: use pid list to load mountinfo --- src/libsysprof-analyze/sysprof-document.c | 27 +++++++++++++---------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index e5a9989d..788f2aab 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -347,7 +347,7 @@ sysprof_document_load_mountinfo (SysprofDocument *self, g_assert (contents != NULL); g_assert (contents[contents_len] == 0); - process_info = _sysprof_document_process_info (self, pid, FALSE); + process_info = _sysprof_document_process_info (self, pid, TRUE); g_assert (process_info != NULL); g_assert (process_info->mount_namespace != NULL); @@ -367,23 +367,26 @@ sysprof_document_load_mountinfo (SysprofDocument *self, static void sysprof_document_load_mountinfos (SysprofDocument *self) { - GHashTableIter hiter; - gpointer key, value; + GtkBitsetIter iter; + guint pid; g_assert (SYSPROF_IS_DOCUMENT (self)); - g_hash_table_iter_init (&hiter, self->pid_to_process_info); - while (g_hash_table_iter_next (&hiter, &key, &value)) + if (gtk_bitset_iter_init_first (&iter, self->pids, &pid)) { - int pid = GPOINTER_TO_INT (key); - g_autofree char *path = g_strdup_printf ("/proc/%d/mountinfo", pid); - g_autoptr(SysprofDocumentFile) file = sysprof_document_lookup_file (self, path); - - if (file != NULL) + do { - g_autoptr(GBytes) bytes = sysprof_document_file_dup_bytes (file); - sysprof_document_load_mountinfo (self, pid, bytes); + g_autofree char *path = g_strdup_printf ("/proc/%u/mountinfo", pid); + g_autoptr(SysprofDocumentFile) file = sysprof_document_lookup_file (self, path); + + if (file != NULL) + { + g_autoptr(GBytes) bytes = sysprof_document_file_dup_bytes (file); + + sysprof_document_load_mountinfo (self, pid, bytes); + } } + while (gtk_bitset_iter_next (&iter, &pid)); } } From 35f87b612183e1dc3b14387e9b48e3a6e6827f37 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 11 May 2023 15:13:23 -0700 Subject: [PATCH 0110/1030] libsysprof-analyze: add superblock-options property This lets you get the full string that was parsed from the mountinfo rather than having to go through our yet-to-be-implemented specific option API. --- src/libsysprof-analyze/sysprof-mount.c | 50 ++++++++++++++++++- src/libsysprof-analyze/sysprof-mount.h | 22 ++++---- .../tests/test-list-processes.c | 7 ++- 3 files changed, 65 insertions(+), 14 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-mount.c b/src/libsysprof-analyze/sysprof-mount.c index ec42dab9..362c4344 100644 --- a/src/libsysprof-analyze/sysprof-mount.c +++ b/src/libsysprof-analyze/sysprof-mount.c @@ -42,12 +42,13 @@ enum { PROP_0, PROP_DEVICE_MAJOR, PROP_DEVICE_MINOR, - PROP_ROOT, + PROP_FILESYSTEM_TYPE, PROP_MOUNT_ID, PROP_MOUNT_POINT, PROP_MOUNT_SOURCE, PROP_PARENT_MOUNT_ID, - PROP_FILESYSTEM_TYPE, + PROP_ROOT, + PROP_SUPERBLOCK_OPTIONS, N_PROPS }; @@ -111,6 +112,10 @@ sysprof_mount_get_property (GObject *object, g_value_set_string (value, sysprof_mount_get_filesystem_type (self)); break; + case PROP_SUPERBLOCK_OPTIONS: + g_value_set_string (value, sysprof_mount_get_superblock_options (self)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } @@ -164,6 +169,11 @@ sysprof_mount_class_init (SysprofMountClass *klass) NULL, (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + properties[PROP_SUPERBLOCK_OPTIONS] = + g_param_spec_string ("superblock-options", NULL, NULL, + NULL, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_properties (object_class, N_PROPS, properties); } @@ -179,6 +189,7 @@ sysprof_mount_new_for_mountinfo (SysprofStrings *strings, g_autoptr(SysprofMount) self = NULL; g_auto(GStrv) parts = NULL; gsize n_parts; + guint i; g_return_val_if_fail (strings != NULL, NULL); g_return_val_if_fail (mountinfo != NULL, NULL); @@ -196,6 +207,35 @@ sysprof_mount_new_for_mountinfo (SysprofStrings *strings, self->root = sysprof_strings_get (strings, parts[3]); self->mount_point = sysprof_strings_get (strings, parts[4]); + /* Skip forward to end of optional fields */ + for (i = 5; parts[i]; i++) + { + if (strcmp (parts[i], "-") == 0) + break; + } + + if (parts[i] == NULL) + goto finish; + + /* filesystem-type column */ + i++; + if (parts[i] == NULL) + goto finish; + self->filesystem_type = sysprof_strings_get (strings, parts[i]); + + /* mount-source column */ + i++; + if (parts[i] == NULL) + goto finish; + self->mount_source = sysprof_strings_get (strings, parts[i]); + + /* superblock options column */ + i++; + if (parts[i] == NULL) + goto finish; + self->superblock_options = sysprof_strings_get (strings, parts[i]); + +finish: return g_steal_pointer (&self); } @@ -235,6 +275,12 @@ sysprof_mount_get_filesystem_type (SysprofMount *self) return self->filesystem_type; } +const char * +sysprof_mount_get_superblock_options (SysprofMount *self) +{ + return self->superblock_options; +} + const char * sysprof_mount_get_superblock_option (SysprofMount *self, const char *option) diff --git a/src/libsysprof-analyze/sysprof-mount.h b/src/libsysprof-analyze/sysprof-mount.h index c7ee83ff..2cf5e9ef 100644 --- a/src/libsysprof-analyze/sysprof-mount.h +++ b/src/libsysprof-analyze/sysprof-mount.h @@ -32,23 +32,25 @@ SYSPROF_AVAILABLE_IN_ALL G_DECLARE_FINAL_TYPE (SysprofMount, sysprof_mount, SYSPROF, MOUNT, GObject) SYSPROF_AVAILABLE_IN_ALL -int sysprof_mount_get_device_major (SysprofMount *self); +int sysprof_mount_get_device_major (SysprofMount *self); SYSPROF_AVAILABLE_IN_ALL -int sysprof_mount_get_device_minor (SysprofMount *self); +int sysprof_mount_get_device_minor (SysprofMount *self); SYSPROF_AVAILABLE_IN_ALL -int sysprof_mount_get_mount_id (SysprofMount *self); +int sysprof_mount_get_mount_id (SysprofMount *self); SYSPROF_AVAILABLE_IN_ALL -int sysprof_mount_get_parent_mount_id (SysprofMount *self); +int sysprof_mount_get_parent_mount_id (SysprofMount *self); SYSPROF_AVAILABLE_IN_ALL -const char *sysprof_mount_get_root (SysprofMount *self); +const char *sysprof_mount_get_root (SysprofMount *self); SYSPROF_AVAILABLE_IN_ALL -const char *sysprof_mount_get_mount_point (SysprofMount *self); +const char *sysprof_mount_get_mount_point (SysprofMount *self); SYSPROF_AVAILABLE_IN_ALL -const char *sysprof_mount_get_mount_source (SysprofMount *self); +const char *sysprof_mount_get_mount_source (SysprofMount *self); SYSPROF_AVAILABLE_IN_ALL -const char *sysprof_mount_get_filesystem_type (SysprofMount *self); +const char *sysprof_mount_get_filesystem_type (SysprofMount *self); SYSPROF_AVAILABLE_IN_ALL -const char *sysprof_mount_get_superblock_option (SysprofMount *self, - const char *option); +const char *sysprof_mount_get_superblock_options (SysprofMount *self); +SYSPROF_AVAILABLE_IN_ALL +const char *sysprof_mount_get_superblock_option (SysprofMount *self, + const char *option); G_END_DECLS diff --git a/src/libsysprof-analyze/tests/test-list-processes.c b/src/libsysprof-analyze/tests/test-list-processes.c index 2006bf32..de9fd9f4 100644 --- a/src/libsysprof-analyze/tests/test-list-processes.c +++ b/src/libsysprof-analyze/tests/test-list-processes.c @@ -74,13 +74,16 @@ main (int argc, { g_autoptr(SysprofMount) mount = g_list_model_get_item (mounts, j); - g_print (" %d %d %d:%d %s %s\n", + g_print (" %d %d %d:%d %s %s %s %s %s\n", sysprof_mount_get_mount_id (mount), sysprof_mount_get_parent_mount_id (mount), sysprof_mount_get_device_major (mount), sysprof_mount_get_device_minor (mount), sysprof_mount_get_root (mount), - sysprof_mount_get_mount_point (mount)); + sysprof_mount_get_mount_point (mount), + sysprof_mount_get_mount_source (mount), + sysprof_mount_get_filesystem_type (mount), + sysprof_mount_get_superblock_options (mount)); } } From ff1b4d00bd10f3ff962028b07e540cf17bd66d8e Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 11 May 2023 15:21:36 -0700 Subject: [PATCH 0111/1030] libsysprof-analyze: implement key/value superblock option API This currently gets used in libsysprof to try to cross mount namespaces to somewhere we can access a binary on the same subvolume. --- src/libsysprof-analyze/sysprof-mount.c | 34 ++++++++++++++++++- src/libsysprof-analyze/sysprof-mount.h | 2 +- .../tests/test-list-processes.c | 4 +++ 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-mount.c b/src/libsysprof-analyze/sysprof-mount.c index 362c4344..b5b4f2f7 100644 --- a/src/libsysprof-analyze/sysprof-mount.c +++ b/src/libsysprof-analyze/sysprof-mount.c @@ -281,10 +281,42 @@ sysprof_mount_get_superblock_options (SysprofMount *self) return self->superblock_options; } -const char * +char * sysprof_mount_get_superblock_option (SysprofMount *self, const char *option) { + gsize option_len; + + g_return_val_if_fail (SYSPROF_IS_MOUNT (self), NULL); + g_return_val_if_fail (option != NULL, NULL); + + if (self->superblock_options == NULL) + return NULL; + + option_len = strlen (option); + + for (const char *c = strstr (self->superblock_options, option); + c != NULL; + c = strstr (c+1, option)) + { + if ((c == self->superblock_options || c[-1] == ',') && + (c[option_len] == 0 || c[option_len] == '=')) + { + const char *value; + const char *endptr; + + if (c[option_len] == 0) + return g_strdup (""); + + value = &c[option_len+1]; + + if (!(endptr = strchr (value, ','))) + return g_strdup (value); + + return g_strndup (value, endptr - value); + } + } + return NULL; } diff --git a/src/libsysprof-analyze/sysprof-mount.h b/src/libsysprof-analyze/sysprof-mount.h index 2cf5e9ef..91e11c8f 100644 --- a/src/libsysprof-analyze/sysprof-mount.h +++ b/src/libsysprof-analyze/sysprof-mount.h @@ -50,7 +50,7 @@ const char *sysprof_mount_get_filesystem_type (SysprofMount *self); SYSPROF_AVAILABLE_IN_ALL const char *sysprof_mount_get_superblock_options (SysprofMount *self); SYSPROF_AVAILABLE_IN_ALL -const char *sysprof_mount_get_superblock_option (SysprofMount *self, +char *sysprof_mount_get_superblock_option (SysprofMount *self, const char *option); G_END_DECLS diff --git a/src/libsysprof-analyze/tests/test-list-processes.c b/src/libsysprof-analyze/tests/test-list-processes.c index de9fd9f4..7b1c8a45 100644 --- a/src/libsysprof-analyze/tests/test-list-processes.c +++ b/src/libsysprof-analyze/tests/test-list-processes.c @@ -73,6 +73,7 @@ main (int argc, for (guint j = 0; j < n_mounts; j++) { g_autoptr(SysprofMount) mount = g_list_model_get_item (mounts, j); + g_autofree char *subvol = sysprof_mount_get_superblock_option (mount, "subvol"); g_print (" %d %d %d:%d %s %s %s %s %s\n", sysprof_mount_get_mount_id (mount), @@ -84,6 +85,9 @@ main (int argc, sysprof_mount_get_mount_source (mount), sysprof_mount_get_filesystem_type (mount), sysprof_mount_get_superblock_options (mount)); + + if (subvol != NULL) + g_print (" Had subvol superblock option: %s\n", subvol); } } From 2d1bf107e518c384573cd4ee0ad0f100e15012d3 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 11 May 2023 15:37:59 -0700 Subject: [PATCH 0112/1030] libsysprof-analyze: handle section edge better --- src/libsysprof-analyze/sysprof-bundled-symbolizer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libsysprof-analyze/sysprof-bundled-symbolizer.c b/src/libsysprof-analyze/sysprof-bundled-symbolizer.c index 7f3c855b..a4533dab 100644 --- a/src/libsysprof-analyze/sysprof-bundled-symbolizer.c +++ b/src/libsysprof-analyze/sysprof-bundled-symbolizer.c @@ -159,7 +159,7 @@ search_for_symbol_cb (gconstpointer a, if (key->addr_begin < ele->addr_begin) return -1; - if (key->addr_begin > ele->addr_end) + if (key->addr_begin >= ele->addr_end) return 1; g_assert (key->addr_begin >= ele->addr_begin); From c0bc3c047ad9bd137af8415de4cf3ce6415d0289 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 11 May 2023 15:38:14 -0700 Subject: [PATCH 0113/1030] libsysprof-analyze: get stack addresses as group --- src/libsysprof-analyze/tests/test-symbolize.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/libsysprof-analyze/tests/test-symbolize.c b/src/libsysprof-analyze/tests/test-symbolize.c index d1ea5761..3f4223a5 100644 --- a/src/libsysprof-analyze/tests/test-symbolize.c +++ b/src/libsysprof-analyze/tests/test-symbolize.c @@ -13,6 +13,7 @@ symbolize_cb (GObject *object, g_autoptr(SysprofDocumentSymbols) symbols = NULL; g_autoptr(GListModel) traceables = NULL; g_autoptr(GError) error = NULL; + SysprofAddress addresses[128]; guint n_items; g_assert (SYSPROF_IS_DOCUMENT (document)); @@ -38,11 +39,16 @@ symbolize_cb (GObject *object, depth = sysprof_document_traceable_get_stack_depth (traceable); pid = sysprof_document_frame_get_pid (SYSPROF_DOCUMENT_FRAME (traceable)); - g_print ("%s depth=%u\n", G_OBJECT_TYPE_NAME (traceable), depth); + g_print ("%s depth=%u pid=%u\n", + G_OBJECT_TYPE_NAME (traceable), + depth, + pid); + + sysprof_document_traceable_get_stack_addresses (traceable, addresses, G_N_ELEMENTS (addresses)); for (guint j = 0; j < depth; j++) { - SysprofAddress address = sysprof_document_traceable_get_stack_address (traceable, j); + SysprofAddress address = addresses[j]; SysprofSymbol *symbol = sysprof_document_symbols_lookup (symbols, pid, last_context, address); SysprofAddressContext context; @@ -56,7 +62,7 @@ symbolize_cb (GObject *object, sysprof_symbol_get_name (symbol), sysprof_symbol_get_binary_path (symbol) ?: ""); else - g_print (" %02d: %p\n", j, GSIZE_TO_POINTER (address)); + g_print (" %02d: %p:\n", j, GSIZE_TO_POINTER (address)); } g_print (" ================\n"); From 8c178305224d4d0b18750cc95a03a3f1c1e8e9f0 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 11 May 2023 15:48:13 -0700 Subject: [PATCH 0114/1030] libsysprof-analyze: resolve via symbol cache for pid --- src/libsysprof-analyze/sysprof-document-symbols.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/libsysprof-analyze/sysprof-document-symbols.c b/src/libsysprof-analyze/sysprof-document-symbols.c index 2f06a79d..b087d0ab 100644 --- a/src/libsysprof-analyze/sysprof-document-symbols.c +++ b/src/libsysprof-analyze/sysprof-document-symbols.c @@ -33,6 +33,7 @@ struct _SysprofDocumentSymbols { GObject parent_instance; SysprofSymbol *context_switches[SYSPROF_ADDRESS_CONTEXT_GUEST_USER+1]; + GHashTable *pid_to_process_info; }; G_DEFINE_FINAL_TYPE (SysprofDocumentSymbols, sysprof_document_symbols, G_TYPE_OBJECT) @@ -45,6 +46,8 @@ sysprof_document_symbols_finalize (GObject *object) for (guint i = 0; i < G_N_ELEMENTS (self->context_switches); i++) g_clear_object (&self->context_switches[i]); + g_clear_pointer (&self->pid_to_process_info, g_hash_table_unref); + G_OBJECT_CLASS (sysprof_document_symbols_parent_class)->finalize (object); } @@ -193,6 +196,8 @@ sysprof_document_symbols_worker (GTask *task, while (gtk_bitset_iter_next (&iter, &i)); } + state->symbols->pid_to_process_info = g_hash_table_ref (state->pid_to_process_info); + g_task_return_pointer (task, g_object_ref (state->symbols), g_object_unref); @@ -255,12 +260,20 @@ sysprof_document_symbols_lookup (SysprofDocumentSymbols *self, SysprofAddress address) { SysprofAddressContext new_context; + SysprofProcessInfo *process_info; g_return_val_if_fail (SYSPROF_IS_DOCUMENT_SYMBOLS (self), NULL); g_return_val_if_fail (context <= SYSPROF_ADDRESS_CONTEXT_GUEST_USER, NULL); + /* TODO: Much better to do decoding in a group of addresses than + * one at a time, so should change this interface a bit. + */ + if (sysprof_address_is_context_switch (address, &new_context)) return self->context_switches[new_context]; - return NULL; + if (!(process_info = g_hash_table_lookup (self->pid_to_process_info, GINT_TO_POINTER (pid)))) + return NULL; + + return sysprof_symbol_cache_lookup (process_info->symbol_cache, address); } From dc99b46254e931dc266aedace4b6e911be0a60a3 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 12 May 2023 14:02:46 -0700 Subject: [PATCH 0115/1030] libsysprof-capture: fix leak of mapped ring buffer structure --- src/libsysprof-capture/mapped-ring-buffer.c | 2 ++ src/tests/test-mapped-ring-buffer.c | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/src/libsysprof-capture/mapped-ring-buffer.c b/src/libsysprof-capture/mapped-ring-buffer.c index c7f2ce2c..ef55c1c1 100644 --- a/src/libsysprof-capture/mapped-ring-buffer.c +++ b/src/libsysprof-capture/mapped-ring-buffer.c @@ -335,6 +335,8 @@ mapped_ring_buffer_finalize (MappedRingBuffer *self) close (self->fd); self->fd = -1; } + + free (self); } void diff --git a/src/tests/test-mapped-ring-buffer.c b/src/tests/test-mapped-ring-buffer.c index 707c0dcb..444df156 100644 --- a/src/tests/test-mapped-ring-buffer.c +++ b/src/tests/test-mapped-ring-buffer.c @@ -163,6 +163,9 @@ test_threaded_movements (void) g_thread_join (thread1); g_thread_join (thread2); + + mapped_ring_buffer_unref (writer); + mapped_ring_buffer_unref (reader); } static void @@ -183,6 +186,8 @@ test_readwrite (void) mapped_ring_buffer_advance (ring, sizeof *ptr); } mapped_ring_buffer_drain (ring, drain_count_cb, NULL); + + mapped_ring_buffer_unref (ring); } gint From 582986f5c99be1d161a37a791222b974c54dd462 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 12 May 2023 14:09:43 -0700 Subject: [PATCH 0116/1030] libsysprof-capture: free deduplicated array entries --- src/libsysprof-capture/sysprof-capture-reader.c | 7 +++++-- src/tests/test-capture.c | 3 ++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/libsysprof-capture/sysprof-capture-reader.c b/src/libsysprof-capture/sysprof-capture-reader.c index 8f5e02e9..6b7fa051 100644 --- a/src/libsysprof-capture/sysprof-capture-reader.c +++ b/src/libsysprof-capture/sysprof-capture-reader.c @@ -1343,7 +1343,7 @@ array_append (const char ***files, *files = new_files; } - (*files)[*n_files] = new_element ? strdup (new_element) : NULL; + (*files)[*n_files] = new_element ? sysprof_strdup (new_element) : NULL; *n_files = *n_files + 1; assert (*n_files <= *n_files_allocated); @@ -1362,7 +1362,10 @@ array_deduplicate (const char **files, for (last_written = 0, next_to_read = 1; last_written <= next_to_read && next_to_read < *n_files;) { if (strcmp (files[next_to_read], files[last_written]) == 0) - next_to_read++; + { + free ((char *)files[next_to_read]); + next_to_read++; + } else files[++last_written] = files[next_to_read++]; } diff --git a/src/tests/test-capture.c b/src/tests/test-capture.c index c06ad209..a59dcab4 100644 --- a/src/tests/test-capture.c +++ b/src/tests/test-capture.c @@ -732,7 +732,7 @@ test_reader_writer_file (void) g_autofree gchar *data = NULL; g_autofree gchar *testfile = NULL; GByteArray *buf = g_byte_array_new (); - g_autofree const gchar **files = NULL; + const char **files; SysprofCaptureWriter *writer; SysprofCaptureReader *reader; SysprofCaptureFrameType type; @@ -799,6 +799,7 @@ test_reader_writer_file (void) g_assert_nonnull (files); g_assert_cmpstr (files[0], ==, testfile); g_assert_null (files[1]); + free (files); sysprof_capture_reader_reset (reader); new_fd = sysprof_memfd_create ("[sysprof-capture-file]"); From 3a8471906fd876f046fc05f867660d7f3730f9dd Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 12 May 2023 14:11:17 -0700 Subject: [PATCH 0117/1030] libsysprof-analyze: build static library for testing internals Otherwise we can't test our private API which very much will need it to ensure correctness. --- src/libsysprof-analyze/meson.build | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build index f242601d..142b21fc 100644 --- a/src/libsysprof-analyze/meson.build +++ b/src/libsysprof-analyze/meson.build @@ -62,13 +62,27 @@ libsysprof_analyze_deps = [ libsysprof_capture_dep, ] -libsysprof_analyze = library('sysprof-analyze-@0@'.format(soname_major_version), - libsysprof_analyze_public_sources + - libsysprof_analyze_private_sources, +libsysprof_analyze_static = static_library( + 'sysprof-analyze-@0@'.format(soname_major_version), + libsysprof_analyze_public_sources + + libsysprof_analyze_private_sources, + include_directories: [include_directories('.'), ipc_include_dirs, libsysprof_capture_include_dirs], dependencies: libsysprof_analyze_deps, + gnu_symbol_visibility: 'hidden', +) + +libsysprof_analyze_static_dep = declare_dependency( + link_with: libsysprof_analyze_static, + dependencies: libsysprof_analyze_deps, + include_directories: [include_directories('.'), + libsysprof_capture_include_dirs], +) + +libsysprof_analyze = library('sysprof-analyze-@0@'.format(soname_major_version), + dependencies: [libsysprof_analyze_static_dep], gnu_symbol_visibility: 'hidden', version: '@0@.0.0'.format(soname_major_version), darwin_versions: '@0@.0'.format(soname_major_version), From 379db7734949b09a26bc953ba2b307cc450fc4ed Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 12 May 2023 14:12:33 -0700 Subject: [PATCH 0118/1030] libsysprof-analyze: add sys/tree.h This pulls in the venerable sys/tree.h so that we may use it in some upcoming cache code. --- src/libsysprof-analyze/tree.h | 1004 +++++++++++++++++++++++++++++++++ 1 file changed, 1004 insertions(+) create mode 100644 src/libsysprof-analyze/tree.h diff --git a/src/libsysprof-analyze/tree.h b/src/libsysprof-analyze/tree.h new file mode 100644 index 00000000..d61f1313 --- /dev/null +++ b/src/libsysprof-analyze/tree.h @@ -0,0 +1,1004 @@ +/* $OpenBSD: tree.h,v 1.31 2023/03/08 04:43:09 guenther Exp $ */ +/* + * Copyright 2002 Niels Provos + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SYS_TREE_H_ +#define _SYS_TREE_H_ + +#include + +/* + * This file defines data structures for different types of trees: + * splay trees and red-black trees. + * + * A splay tree is a self-organizing data structure. Every operation + * on the tree causes a splay to happen. The splay moves the requested + * node to the root of the tree and partly rebalances it. + * + * This has the benefit that request locality causes faster lookups as + * the requested nodes move to the top of the tree. On the other hand, + * every lookup causes memory writes. + * + * The Balance Theorem bounds the total access time for m operations + * and n inserts on an initially empty tree as O((m + n)lg n). The + * amortized cost for a sequence of m accesses to a splay tree is O(lg n); + * + * A red-black tree is a binary search tree with the node color as an + * extra attribute. It fulfills a set of conditions: + * - every search path from the root to a leaf consists of the + * same number of black nodes, + * - each red node (except for the root) has a black parent, + * - each leaf node is black. + * + * Every operation on a red-black tree is bounded as O(lg n). + * The maximum height of a red-black tree is 2lg (n+1). + */ + +#define SPLAY_HEAD(name, type) \ +struct name { \ + struct type *sph_root; /* root of the tree */ \ +} + +#define SPLAY_INITIALIZER(root) \ + { NULL } + +#define SPLAY_INIT(root) do { \ + (root)->sph_root = NULL; \ +} while (0) + +#define SPLAY_ENTRY(type) \ +struct { \ + struct type *spe_left; /* left element */ \ + struct type *spe_right; /* right element */ \ +} + +#define SPLAY_LEFT(elm, field) (elm)->field.spe_left +#define SPLAY_RIGHT(elm, field) (elm)->field.spe_right +#define SPLAY_ROOT(head) (head)->sph_root +#define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL) + +/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */ +#define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \ + SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \ + SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ + (head)->sph_root = tmp; \ +} while (0) + +#define SPLAY_ROTATE_LEFT(head, tmp, field) do { \ + SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \ + SPLAY_LEFT(tmp, field) = (head)->sph_root; \ + (head)->sph_root = tmp; \ +} while (0) + +#define SPLAY_LINKLEFT(head, tmp, field) do { \ + SPLAY_LEFT(tmp, field) = (head)->sph_root; \ + tmp = (head)->sph_root; \ + (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \ +} while (0) + +#define SPLAY_LINKRIGHT(head, tmp, field) do { \ + SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ + tmp = (head)->sph_root; \ + (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \ +} while (0) + +#define SPLAY_ASSEMBLE(head, node, left, right, field) do { \ + SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \ + SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\ + SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \ + SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \ +} while (0) + +/* Generates prototypes and inline functions */ + +#define SPLAY_PROTOTYPE(name, type, field, cmp) \ +void name##_SPLAY(struct name *, struct type *); \ +void name##_SPLAY_MINMAX(struct name *, int); \ +struct type *name##_SPLAY_INSERT(struct name *, struct type *); \ +struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \ + \ +/* Finds the node with the same key as elm */ \ +static __unused __inline struct type * \ +name##_SPLAY_FIND(struct name *head, struct type *elm) \ +{ \ + if (SPLAY_EMPTY(head)) \ + return(NULL); \ + name##_SPLAY(head, elm); \ + if ((cmp)(elm, (head)->sph_root) == 0) \ + return (head->sph_root); \ + return (NULL); \ +} \ + \ +static __unused __inline struct type * \ +name##_SPLAY_NEXT(struct name *head, struct type *elm) \ +{ \ + name##_SPLAY(head, elm); \ + if (SPLAY_RIGHT(elm, field) != NULL) { \ + elm = SPLAY_RIGHT(elm, field); \ + while (SPLAY_LEFT(elm, field) != NULL) { \ + elm = SPLAY_LEFT(elm, field); \ + } \ + } else \ + elm = NULL; \ + return (elm); \ +} \ + \ +static __unused __inline struct type * \ +name##_SPLAY_MIN_MAX(struct name *head, int val) \ +{ \ + name##_SPLAY_MINMAX(head, val); \ + return (SPLAY_ROOT(head)); \ +} + +/* Main splay operation. + * Moves node close to the key of elm to top + */ +#define SPLAY_GENERATE(name, type, field, cmp) \ +struct type * \ +name##_SPLAY_INSERT(struct name *head, struct type *elm) \ +{ \ + if (SPLAY_EMPTY(head)) { \ + SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \ + } else { \ + int __comp; \ + name##_SPLAY(head, elm); \ + __comp = (cmp)(elm, (head)->sph_root); \ + if(__comp < 0) { \ + SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\ + SPLAY_RIGHT(elm, field) = (head)->sph_root; \ + SPLAY_LEFT((head)->sph_root, field) = NULL; \ + } else if (__comp > 0) { \ + SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\ + SPLAY_LEFT(elm, field) = (head)->sph_root; \ + SPLAY_RIGHT((head)->sph_root, field) = NULL; \ + } else \ + return ((head)->sph_root); \ + } \ + (head)->sph_root = (elm); \ + return (NULL); \ +} \ + \ +struct type * \ +name##_SPLAY_REMOVE(struct name *head, struct type *elm) \ +{ \ + struct type *__tmp; \ + if (SPLAY_EMPTY(head)) \ + return (NULL); \ + name##_SPLAY(head, elm); \ + if ((cmp)(elm, (head)->sph_root) == 0) { \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \ + (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\ + } else { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\ + name##_SPLAY(head, elm); \ + SPLAY_RIGHT((head)->sph_root, field) = __tmp; \ + } \ + return (elm); \ + } \ + return (NULL); \ +} \ + \ +void \ +name##_SPLAY(struct name *head, struct type *elm) \ +{ \ + struct type __node, *__left, *__right, *__tmp; \ + int __comp; \ +\ + SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ + __left = __right = &__node; \ +\ + while ((__comp = (cmp)(elm, (head)->sph_root))) { \ + if (__comp < 0) { \ + __tmp = SPLAY_LEFT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if ((cmp)(elm, __tmp) < 0){ \ + SPLAY_ROTATE_RIGHT(head, __tmp, field); \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKLEFT(head, __right, field); \ + } else if (__comp > 0) { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if ((cmp)(elm, __tmp) > 0){ \ + SPLAY_ROTATE_LEFT(head, __tmp, field); \ + if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKRIGHT(head, __left, field); \ + } \ + } \ + SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ +} \ + \ +/* Splay with either the minimum or the maximum element \ + * Used to find minimum or maximum element in tree. \ + */ \ +void name##_SPLAY_MINMAX(struct name *head, int __comp) \ +{ \ + struct type __node, *__left, *__right, *__tmp; \ +\ + SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ + __left = __right = &__node; \ +\ + while (1) { \ + if (__comp < 0) { \ + __tmp = SPLAY_LEFT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if (__comp < 0){ \ + SPLAY_ROTATE_RIGHT(head, __tmp, field); \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKLEFT(head, __right, field); \ + } else if (__comp > 0) { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if (__comp > 0) { \ + SPLAY_ROTATE_LEFT(head, __tmp, field); \ + if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKRIGHT(head, __left, field); \ + } \ + } \ + SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ +} + +#define SPLAY_NEGINF -1 +#define SPLAY_INF 1 + +#define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y) +#define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y) +#define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y) +#define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y) +#define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \ + : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF)) +#define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \ + : name##_SPLAY_MIN_MAX(x, SPLAY_INF)) + +#define SPLAY_FOREACH(x, name, head) \ + for ((x) = SPLAY_MIN(name, head); \ + (x) != NULL; \ + (x) = SPLAY_NEXT(name, head, x)) + +/* Macros that define a red-black tree */ +#define RB_HEAD(name, type) \ +struct name { \ + struct type *rbh_root; /* root of the tree */ \ +} + +#define RB_INITIALIZER(root) \ + { NULL } + +#define RB_INIT(root) do { \ + (root)->rbh_root = NULL; \ +} while (0) + +#define RB_BLACK 0 +#define RB_RED 1 +#define RB_ENTRY(type) \ +struct { \ + struct type *rbe_left; /* left element */ \ + struct type *rbe_right; /* right element */ \ + struct type *rbe_parent; /* parent element */ \ + int rbe_color; /* node color */ \ +} + +#define RB_LEFT(elm, field) (elm)->field.rbe_left +#define RB_RIGHT(elm, field) (elm)->field.rbe_right +#define RB_PARENT(elm, field) (elm)->field.rbe_parent +#define RB_COLOR(elm, field) (elm)->field.rbe_color +#define RB_ROOT(head) (head)->rbh_root +#define RB_EMPTY(head) (RB_ROOT(head) == NULL) + +#define RB_SET(elm, parent, field) do { \ + RB_PARENT(elm, field) = parent; \ + RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \ + RB_COLOR(elm, field) = RB_RED; \ +} while (0) + +#define RB_SET_BLACKRED(black, red, field) do { \ + RB_COLOR(black, field) = RB_BLACK; \ + RB_COLOR(red, field) = RB_RED; \ +} while (0) + +#ifndef RB_AUGMENT +#define RB_AUGMENT(x) do {} while (0) +#endif + +#define RB_ROTATE_LEFT(head, elm, tmp, field) do { \ + (tmp) = RB_RIGHT(elm, field); \ + if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field))) { \ + RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \ + } \ + RB_AUGMENT(elm); \ + if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \ + if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ + RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ + else \ + RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ + } else \ + (head)->rbh_root = (tmp); \ + RB_LEFT(tmp, field) = (elm); \ + RB_PARENT(elm, field) = (tmp); \ + RB_AUGMENT(tmp); \ + if ((RB_PARENT(tmp, field))) \ + RB_AUGMENT(RB_PARENT(tmp, field)); \ +} while (0) + +#define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \ + (tmp) = RB_LEFT(elm, field); \ + if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field))) { \ + RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \ + } \ + RB_AUGMENT(elm); \ + if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \ + if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ + RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ + else \ + RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ + } else \ + (head)->rbh_root = (tmp); \ + RB_RIGHT(tmp, field) = (elm); \ + RB_PARENT(elm, field) = (tmp); \ + RB_AUGMENT(tmp); \ + if ((RB_PARENT(tmp, field))) \ + RB_AUGMENT(RB_PARENT(tmp, field)); \ +} while (0) + +/* Generates prototypes and inline functions */ +#define RB_PROTOTYPE(name, type, field, cmp) \ + RB_PROTOTYPE_INTERNAL(name, type, field, cmp,) +#define RB_PROTOTYPE_STATIC(name, type, field, cmp) \ + RB_PROTOTYPE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static) +#define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \ +attr void name##_RB_INSERT_COLOR(struct name *, struct type *); \ +attr void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\ +attr struct type *name##_RB_REMOVE(struct name *, struct type *); \ +attr struct type *name##_RB_INSERT(struct name *, struct type *); \ +attr struct type *name##_RB_FIND(struct name *, struct type *); \ +attr struct type *name##_RB_NFIND(struct name *, struct type *); \ +attr struct type *name##_RB_NEXT(struct type *); \ +attr struct type *name##_RB_PREV(struct type *); \ +attr struct type *name##_RB_MINMAX(struct name *, int); \ + \ + +/* Main rb operation. + * Moves node close to the key of elm to top + */ +#define RB_GENERATE(name, type, field, cmp) \ + RB_GENERATE_INTERNAL(name, type, field, cmp,) +#define RB_GENERATE_STATIC(name, type, field, cmp) \ + RB_GENERATE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static) +#define RB_GENERATE_INTERNAL(name, type, field, cmp, attr) \ +attr void \ +name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \ +{ \ + struct type *parent, *gparent, *tmp; \ + while ((parent = RB_PARENT(elm, field)) && \ + RB_COLOR(parent, field) == RB_RED) { \ + gparent = RB_PARENT(parent, field); \ + if (parent == RB_LEFT(gparent, field)) { \ + tmp = RB_RIGHT(gparent, field); \ + if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ + RB_COLOR(tmp, field) = RB_BLACK; \ + RB_SET_BLACKRED(parent, gparent, field);\ + elm = gparent; \ + continue; \ + } \ + if (RB_RIGHT(parent, field) == elm) { \ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + tmp = parent; \ + parent = elm; \ + elm = tmp; \ + } \ + RB_SET_BLACKRED(parent, gparent, field); \ + RB_ROTATE_RIGHT(head, gparent, tmp, field); \ + } else { \ + tmp = RB_LEFT(gparent, field); \ + if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ + RB_COLOR(tmp, field) = RB_BLACK; \ + RB_SET_BLACKRED(parent, gparent, field);\ + elm = gparent; \ + continue; \ + } \ + if (RB_LEFT(parent, field) == elm) { \ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + tmp = parent; \ + parent = elm; \ + elm = tmp; \ + } \ + RB_SET_BLACKRED(parent, gparent, field); \ + RB_ROTATE_LEFT(head, gparent, tmp, field); \ + } \ + } \ + RB_COLOR(head->rbh_root, field) = RB_BLACK; \ +} \ + \ +attr void \ +name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \ +{ \ + struct type *tmp; \ + while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \ + elm != RB_ROOT(head)) { \ + if (RB_LEFT(parent, field) == elm) { \ + tmp = RB_RIGHT(parent, field); \ + if (RB_COLOR(tmp, field) == RB_RED) { \ + RB_SET_BLACKRED(tmp, parent, field); \ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + tmp = RB_RIGHT(parent, field); \ + } \ + if ((RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ + (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ + RB_COLOR(tmp, field) = RB_RED; \ + elm = parent; \ + parent = RB_PARENT(elm, field); \ + } else { \ + if (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\ + struct type *oleft; \ + if ((oleft = RB_LEFT(tmp, field)))\ + RB_COLOR(oleft, field) = RB_BLACK;\ + RB_COLOR(tmp, field) = RB_RED; \ + RB_ROTATE_RIGHT(head, tmp, oleft, field);\ + tmp = RB_RIGHT(parent, field); \ + } \ + RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ + RB_COLOR(parent, field) = RB_BLACK; \ + if (RB_RIGHT(tmp, field)) \ + RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + elm = RB_ROOT(head); \ + break; \ + } \ + } else { \ + tmp = RB_LEFT(parent, field); \ + if (RB_COLOR(tmp, field) == RB_RED) { \ + RB_SET_BLACKRED(tmp, parent, field); \ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + tmp = RB_LEFT(parent, field); \ + } \ + if ((RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ + (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ + RB_COLOR(tmp, field) = RB_RED; \ + elm = parent; \ + parent = RB_PARENT(elm, field); \ + } else { \ + if (RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\ + struct type *oright; \ + if ((oright = RB_RIGHT(tmp, field)))\ + RB_COLOR(oright, field) = RB_BLACK;\ + RB_COLOR(tmp, field) = RB_RED; \ + RB_ROTATE_LEFT(head, tmp, oright, field);\ + tmp = RB_LEFT(parent, field); \ + } \ + RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ + RB_COLOR(parent, field) = RB_BLACK; \ + if (RB_LEFT(tmp, field)) \ + RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + elm = RB_ROOT(head); \ + break; \ + } \ + } \ + } \ + if (elm) \ + RB_COLOR(elm, field) = RB_BLACK; \ +} \ + \ +attr struct type * \ +name##_RB_REMOVE(struct name *head, struct type *elm) \ +{ \ + struct type *child, *parent, *old = elm; \ + int color; \ + if (RB_LEFT(elm, field) == NULL) \ + child = RB_RIGHT(elm, field); \ + else if (RB_RIGHT(elm, field) == NULL) \ + child = RB_LEFT(elm, field); \ + else { \ + struct type *left; \ + elm = RB_RIGHT(elm, field); \ + while ((left = RB_LEFT(elm, field))) \ + elm = left; \ + child = RB_RIGHT(elm, field); \ + parent = RB_PARENT(elm, field); \ + color = RB_COLOR(elm, field); \ + if (child) \ + RB_PARENT(child, field) = parent; \ + if (parent) { \ + if (RB_LEFT(parent, field) == elm) \ + RB_LEFT(parent, field) = child; \ + else \ + RB_RIGHT(parent, field) = child; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = child; \ + if (RB_PARENT(elm, field) == old) \ + parent = elm; \ + (elm)->field = (old)->field; \ + if (RB_PARENT(old, field)) { \ + if (RB_LEFT(RB_PARENT(old, field), field) == old)\ + RB_LEFT(RB_PARENT(old, field), field) = elm;\ + else \ + RB_RIGHT(RB_PARENT(old, field), field) = elm;\ + RB_AUGMENT(RB_PARENT(old, field)); \ + } else \ + RB_ROOT(head) = elm; \ + RB_PARENT(RB_LEFT(old, field), field) = elm; \ + if (RB_RIGHT(old, field)) \ + RB_PARENT(RB_RIGHT(old, field), field) = elm; \ + if (parent) { \ + left = parent; \ + do { \ + RB_AUGMENT(left); \ + } while ((left = RB_PARENT(left, field))); \ + } \ + goto color; \ + } \ + parent = RB_PARENT(elm, field); \ + color = RB_COLOR(elm, field); \ + if (child) \ + RB_PARENT(child, field) = parent; \ + if (parent) { \ + if (RB_LEFT(parent, field) == elm) \ + RB_LEFT(parent, field) = child; \ + else \ + RB_RIGHT(parent, field) = child; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = child; \ +color: \ + if (color == RB_BLACK) \ + name##_RB_REMOVE_COLOR(head, parent, child); \ + return (old); \ +} \ + \ +/* Inserts a node into the RB tree */ \ +attr struct type * \ +name##_RB_INSERT(struct name *head, struct type *elm) \ +{ \ + struct type *tmp; \ + struct type *parent = NULL; \ + int comp = 0; \ + tmp = RB_ROOT(head); \ + while (tmp) { \ + parent = tmp; \ + comp = (cmp)(elm, parent); \ + if (comp < 0) \ + tmp = RB_LEFT(tmp, field); \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + RB_SET(elm, parent, field); \ + if (parent != NULL) { \ + if (comp < 0) \ + RB_LEFT(parent, field) = elm; \ + else \ + RB_RIGHT(parent, field) = elm; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = elm; \ + name##_RB_INSERT_COLOR(head, elm); \ + return (NULL); \ +} \ + \ +/* Finds the node with the same key as elm */ \ +attr struct type * \ +name##_RB_FIND(struct name *head, struct type *elm) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + int comp; \ + while (tmp) { \ + comp = cmp(elm, tmp); \ + if (comp < 0) \ + tmp = RB_LEFT(tmp, field); \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + return (NULL); \ +} \ + \ +/* Finds the first node greater than or equal to the search key */ \ +attr struct type * \ +name##_RB_NFIND(struct name *head, struct type *elm) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + struct type *res = NULL; \ + int comp; \ + while (tmp) { \ + comp = cmp(elm, tmp); \ + if (comp < 0) { \ + res = tmp; \ + tmp = RB_LEFT(tmp, field); \ + } \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + return (res); \ +} \ + \ +attr struct type * \ +name##_RB_NEXT(struct type *elm) \ +{ \ + if (RB_RIGHT(elm, field)) { \ + elm = RB_RIGHT(elm, field); \ + while (RB_LEFT(elm, field)) \ + elm = RB_LEFT(elm, field); \ + } else { \ + if (RB_PARENT(elm, field) && \ + (elm == RB_LEFT(RB_PARENT(elm, field), field))) \ + elm = RB_PARENT(elm, field); \ + else { \ + while (RB_PARENT(elm, field) && \ + (elm == RB_RIGHT(RB_PARENT(elm, field), field)))\ + elm = RB_PARENT(elm, field); \ + elm = RB_PARENT(elm, field); \ + } \ + } \ + return (elm); \ +} \ + \ +attr struct type * \ +name##_RB_PREV(struct type *elm) \ +{ \ + if (RB_LEFT(elm, field)) { \ + elm = RB_LEFT(elm, field); \ + while (RB_RIGHT(elm, field)) \ + elm = RB_RIGHT(elm, field); \ + } else { \ + if (RB_PARENT(elm, field) && \ + (elm == RB_RIGHT(RB_PARENT(elm, field), field))) \ + elm = RB_PARENT(elm, field); \ + else { \ + while (RB_PARENT(elm, field) && \ + (elm == RB_LEFT(RB_PARENT(elm, field), field)))\ + elm = RB_PARENT(elm, field); \ + elm = RB_PARENT(elm, field); \ + } \ + } \ + return (elm); \ +} \ + \ +attr struct type * \ +name##_RB_MINMAX(struct name *head, int val) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + struct type *parent = NULL; \ + while (tmp) { \ + parent = tmp; \ + if (val < 0) \ + tmp = RB_LEFT(tmp, field); \ + else \ + tmp = RB_RIGHT(tmp, field); \ + } \ + return (parent); \ +} + +#define RB_NEGINF -1 +#define RB_INF 1 + +#define RB_INSERT(name, x, y) name##_RB_INSERT(x, y) +#define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y) +#define RB_FIND(name, x, y) name##_RB_FIND(x, y) +#define RB_NFIND(name, x, y) name##_RB_NFIND(x, y) +#define RB_NEXT(name, x, y) name##_RB_NEXT(y) +#define RB_PREV(name, x, y) name##_RB_PREV(y) +#define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF) +#define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF) + +#define RB_FOREACH(x, name, head) \ + for ((x) = RB_MIN(name, head); \ + (x) != NULL; \ + (x) = name##_RB_NEXT(x)) + +#define RB_FOREACH_SAFE(x, name, head, y) \ + for ((x) = RB_MIN(name, head); \ + ((x) != NULL) && ((y) = name##_RB_NEXT(x), 1); \ + (x) = (y)) + +#define RB_FOREACH_REVERSE(x, name, head) \ + for ((x) = RB_MAX(name, head); \ + (x) != NULL; \ + (x) = name##_RB_PREV(x)) + +#define RB_FOREACH_REVERSE_SAFE(x, name, head, y) \ + for ((x) = RB_MAX(name, head); \ + ((x) != NULL) && ((y) = name##_RB_PREV(x), 1); \ + (x) = (y)) + + +/* + * Copyright (c) 2016 David Gwynne + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +struct rb_type { + int (*t_compare)(const void *, const void *); + void (*t_augment)(void *); + unsigned int t_offset; /* offset of rb_entry in type */ +}; + +struct rb_tree { + struct rb_entry *rbt_root; +}; + +struct rb_entry { + struct rb_entry *rbt_parent; + struct rb_entry *rbt_left; + struct rb_entry *rbt_right; + unsigned int rbt_color; +}; + +#define RBT_HEAD(_name, _type) \ +struct _name { \ + struct rb_tree rbh_root; \ +} + +#define RBT_ENTRY(_type) struct rb_entry + +static inline void +_rb_init(struct rb_tree *rbt) +{ + rbt->rbt_root = NULL; +} + +static inline int +_rb_empty(struct rb_tree *rbt) +{ + return (rbt->rbt_root == NULL); +} + +void *_rb_insert(const struct rb_type *, struct rb_tree *, void *); +void *_rb_remove(const struct rb_type *, struct rb_tree *, void *); +void *_rb_find(const struct rb_type *, struct rb_tree *, const void *); +void *_rb_nfind(const struct rb_type *, struct rb_tree *, const void *); +void *_rb_root(const struct rb_type *, struct rb_tree *); +void *_rb_min(const struct rb_type *, struct rb_tree *); +void *_rb_max(const struct rb_type *, struct rb_tree *); +void *_rb_next(const struct rb_type *, void *); +void *_rb_prev(const struct rb_type *, void *); +void *_rb_left(const struct rb_type *, void *); +void *_rb_right(const struct rb_type *, void *); +void *_rb_parent(const struct rb_type *, void *); +void _rb_set_left(const struct rb_type *, void *, void *); +void _rb_set_right(const struct rb_type *, void *, void *); +void _rb_set_parent(const struct rb_type *, void *, void *); +void _rb_poison(const struct rb_type *, void *, unsigned long); +int _rb_check(const struct rb_type *, void *, unsigned long); + +#define RBT_INITIALIZER(_head) { { NULL } } + +#define RBT_PROTOTYPE(_name, _type, _field, _cmp) \ +extern const struct rb_type *const _name##_RBT_TYPE; \ + \ +__unused static inline void \ +_name##_RBT_INIT(struct _name *head) \ +{ \ + _rb_init(&head->rbh_root); \ +} \ + \ +__unused static inline struct _type * \ +_name##_RBT_INSERT(struct _name *head, struct _type *elm) \ +{ \ + return _rb_insert(_name##_RBT_TYPE, &head->rbh_root, elm); \ +} \ + \ +__unused static inline struct _type * \ +_name##_RBT_REMOVE(struct _name *head, struct _type *elm) \ +{ \ + return _rb_remove(_name##_RBT_TYPE, &head->rbh_root, elm); \ +} \ + \ +__unused static inline struct _type * \ +_name##_RBT_FIND(struct _name *head, const struct _type *key) \ +{ \ + return _rb_find(_name##_RBT_TYPE, &head->rbh_root, key); \ +} \ + \ +__unused static inline struct _type * \ +_name##_RBT_NFIND(struct _name *head, const struct _type *key) \ +{ \ + return _rb_nfind(_name##_RBT_TYPE, &head->rbh_root, key); \ +} \ + \ +__unused static inline struct _type * \ +_name##_RBT_ROOT(struct _name *head) \ +{ \ + return _rb_root(_name##_RBT_TYPE, &head->rbh_root); \ +} \ + \ +__unused static inline int \ +_name##_RBT_EMPTY(struct _name *head) \ +{ \ + return _rb_empty(&head->rbh_root); \ +} \ + \ +__unused static inline struct _type * \ +_name##_RBT_MIN(struct _name *head) \ +{ \ + return _rb_min(_name##_RBT_TYPE, &head->rbh_root); \ +} \ + \ +__unused static inline struct _type * \ +_name##_RBT_MAX(struct _name *head) \ +{ \ + return _rb_max(_name##_RBT_TYPE, &head->rbh_root); \ +} \ + \ +__unused static inline struct _type * \ +_name##_RBT_NEXT(struct _type *elm) \ +{ \ + return _rb_next(_name##_RBT_TYPE, elm); \ +} \ + \ +__unused static inline struct _type * \ +_name##_RBT_PREV(struct _type *elm) \ +{ \ + return _rb_prev(_name##_RBT_TYPE, elm); \ +} \ + \ +__unused static inline struct _type * \ +_name##_RBT_LEFT(struct _type *elm) \ +{ \ + return _rb_left(_name##_RBT_TYPE, elm); \ +} \ + \ +__unused static inline struct _type * \ +_name##_RBT_RIGHT(struct _type *elm) \ +{ \ + return _rb_right(_name##_RBT_TYPE, elm); \ +} \ + \ +__unused static inline struct _type * \ +_name##_RBT_PARENT(struct _type *elm) \ +{ \ + return _rb_parent(_name##_RBT_TYPE, elm); \ +} \ + \ +__unused static inline void \ +_name##_RBT_SET_LEFT(struct _type *elm, struct _type *left) \ +{ \ + _rb_set_left(_name##_RBT_TYPE, elm, left); \ +} \ + \ +__unused static inline void \ +_name##_RBT_SET_RIGHT(struct _type *elm, struct _type *right) \ +{ \ + _rb_set_right(_name##_RBT_TYPE, elm, right); \ +} \ + \ +__unused static inline void \ +_name##_RBT_SET_PARENT(struct _type *elm, struct _type *parent) \ +{ \ + _rb_set_parent(_name##_RBT_TYPE, elm, parent); \ +} \ + \ +__unused static inline void \ +_name##_RBT_POISON(struct _type *elm, unsigned long poison) \ +{ \ + _rb_poison(_name##_RBT_TYPE, elm, poison); \ +} \ + \ +__unused static inline int \ +_name##_RBT_CHECK(struct _type *elm, unsigned long poison) \ +{ \ + return _rb_check(_name##_RBT_TYPE, elm, poison); \ +} + +#define RBT_GENERATE_INTERNAL(_name, _type, _field, _cmp, _aug) \ +static int \ +_name##_RBT_COMPARE(const void *lptr, const void *rptr) \ +{ \ + const struct _type *l = lptr, *r = rptr; \ + return _cmp(l, r); \ +} \ +static const struct rb_type _name##_RBT_INFO = { \ + _name##_RBT_COMPARE, \ + _aug, \ + offsetof(struct _type, _field), \ +}; \ +const struct rb_type *const _name##_RBT_TYPE = &_name##_RBT_INFO + +#define RBT_GENERATE_AUGMENT(_name, _type, _field, _cmp, _aug) \ +static void \ +_name##_RBT_AUGMENT(void *ptr) \ +{ \ + struct _type *p = ptr; \ + return _aug(p); \ +} \ +RBT_GENERATE_INTERNAL(_name, _type, _field, _cmp, _name##_RBT_AUGMENT) + +#define RBT_GENERATE(_name, _type, _field, _cmp) \ + RBT_GENERATE_INTERNAL(_name, _type, _field, _cmp, NULL) + +#define RBT_INIT(_name, _head) _name##_RBT_INIT(_head) +#define RBT_INSERT(_name, _head, _elm) _name##_RBT_INSERT(_head, _elm) +#define RBT_REMOVE(_name, _head, _elm) _name##_RBT_REMOVE(_head, _elm) +#define RBT_FIND(_name, _head, _key) _name##_RBT_FIND(_head, _key) +#define RBT_NFIND(_name, _head, _key) _name##_RBT_NFIND(_head, _key) +#define RBT_ROOT(_name, _head) _name##_RBT_ROOT(_head) +#define RBT_EMPTY(_name, _head) _name##_RBT_EMPTY(_head) +#define RBT_MIN(_name, _head) _name##_RBT_MIN(_head) +#define RBT_MAX(_name, _head) _name##_RBT_MAX(_head) +#define RBT_NEXT(_name, _elm) _name##_RBT_NEXT(_elm) +#define RBT_PREV(_name, _elm) _name##_RBT_PREV(_elm) +#define RBT_LEFT(_name, _elm) _name##_RBT_LEFT(_elm) +#define RBT_RIGHT(_name, _elm) _name##_RBT_RIGHT(_elm) +#define RBT_PARENT(_name, _elm) _name##_RBT_PARENT(_elm) +#define RBT_SET_LEFT(_name, _elm, _l) _name##_RBT_SET_LEFT(_elm, _l) +#define RBT_SET_RIGHT(_name, _elm, _r) _name##_RBT_SET_RIGHT(_elm, _r) +#define RBT_SET_PARENT(_name, _elm, _p) _name##_RBT_SET_PARENT(_elm, _p) +#define RBT_POISON(_name, _elm, _p) _name##_RBT_POISON(_elm, _p) +#define RBT_CHECK(_name, _elm, _p) _name##_RBT_CHECK(_elm, _p) + +#define RBT_FOREACH(_e, _name, _head) \ + for ((_e) = RBT_MIN(_name, (_head)); \ + (_e) != NULL; \ + (_e) = RBT_NEXT(_name, (_e))) + +#define RBT_FOREACH_SAFE(_e, _name, _head, _n) \ + for ((_e) = RBT_MIN(_name, (_head)); \ + (_e) != NULL && ((_n) = RBT_NEXT(_name, (_e)), 1); \ + (_e) = (_n)) + +#define RBT_FOREACH_REVERSE(_e, _name, _head) \ + for ((_e) = RBT_MAX(_name, (_head)); \ + (_e) != NULL; \ + (_e) = RBT_PREV(_name, (_e))) + +#define RBT_FOREACH_REVERSE_SAFE(_e, _name, _head, _n) \ + for ((_e) = RBT_MAX(_name, (_head)); \ + (_e) != NULL && ((_n) = RBT_PREV(_name, (_e)), 1); \ + (_e) = (_n)) + +#endif /* _SYS_TREE_H_ */ From 81c384a9741c0bb3c2d4eaf8f7bd6d9b4e60d50f Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 12 May 2023 14:13:40 -0700 Subject: [PATCH 0119/1030] libsysprof-analyze: use interval tree for symbol cache This uses an augmented red-black tree to create an interval tree with non-interval lookups. That amounts to storing address ranges within the red-black tree, but looking up by single address. --- src/libsysprof-analyze/sysprof-symbol-cache.c | 184 +++++++++++++----- src/libsysprof-analyze/tests/meson.build | 4 +- .../tests/test-symbol-cache.c | 171 ++++++++++++++++ 3 files changed, 310 insertions(+), 49 deletions(-) create mode 100644 src/libsysprof-analyze/tests/test-symbol-cache.c diff --git a/src/libsysprof-analyze/sysprof-symbol-cache.c b/src/libsysprof-analyze/sysprof-symbol-cache.c index 37f4dd88..d4f7b866 100644 --- a/src/libsysprof-analyze/sysprof-symbol-cache.c +++ b/src/libsysprof-analyze/sysprof-symbol-cache.c @@ -20,23 +20,88 @@ #include "config.h" +typedef struct _SysprofSymbolCacheNode SysprofSymbolCacheNode; +static void sysprof_symbol_cache_node_augment (SysprofSymbolCacheNode *node); +#define RB_AUGMENT(elem) sysprof_symbol_cache_node_augment(elem) + +#include "tree.h" + #include "sysprof-symbol-private.h" #include "sysprof-symbol-cache-private.h" +struct _SysprofSymbolCacheNode +{ + RB_ENTRY(_SysprofSymbolCacheNode) link; + SysprofSymbol *symbol; + guint64 low; + guint64 high; + guint64 max; +}; + struct _SysprofSymbolCache { - GObject parent_instance; - GSequence *symbols; + GObject parent_instance; + RB_HEAD(sysprof_symbol_cache, _SysprofSymbolCacheNode) head; }; G_DEFINE_FINAL_TYPE (SysprofSymbolCache, sysprof_symbol_cache, G_TYPE_OBJECT) +static inline int +sysprof_symbol_cache_node_compare (SysprofSymbolCacheNode *a, + SysprofSymbolCacheNode *b) +{ + if (a->low < b->low) + return -1; + else if (a->low > b->low) + return 1; + else + return 0; +} + +RB_GENERATE_STATIC(sysprof_symbol_cache, _SysprofSymbolCacheNode, link, sysprof_symbol_cache_node_compare); + +static void +sysprof_symbol_cache_node_augment (SysprofSymbolCacheNode *node) +{ + node->max = node->high; + + if (RB_LEFT(node, link) && RB_LEFT(node, link)->max > node->max) + node->max = RB_LEFT(node, link)->max; + + if (RB_RIGHT(node, link) && RB_RIGHT(node, link)->max > node->max) + node->max = RB_RIGHT(node, link)->max; +} + +static void +sysprof_symbol_cache_node_finalize (SysprofSymbolCacheNode *node) +{ + g_clear_object (&node->symbol); + g_free (node); +} + +static void +sysprof_symbol_cache_node_free (SysprofSymbolCacheNode *node) +{ + SysprofSymbolCacheNode *right = RB_RIGHT(node, link); + SysprofSymbolCacheNode *left = RB_LEFT(node, link); + + if (left != NULL) + sysprof_symbol_cache_node_free (left); + + sysprof_symbol_cache_node_finalize (node); + + if (right != NULL) + sysprof_symbol_cache_node_free (right); +} + static void sysprof_symbol_cache_finalize (GObject *object) { SysprofSymbolCache *self = (SysprofSymbolCache *)object; + SysprofSymbolCacheNode *node = RB_ROOT(&self->head); - g_clear_pointer (&self->symbols, g_sequence_free); + if (node != NULL) + sysprof_symbol_cache_node_free (node); G_OBJECT_CLASS (sysprof_symbol_cache_parent_class)->finalize (object); } @@ -52,7 +117,7 @@ sysprof_symbol_cache_class_init (SysprofSymbolCacheClass *klass) static void sysprof_symbol_cache_init (SysprofSymbolCache *self) { - self->symbols = g_sequence_new (g_object_unref); + RB_INIT (&self->head); } SysprofSymbolCache * @@ -61,80 +126,105 @@ sysprof_symbol_cache_new (void) return g_object_new (SYSPROF_TYPE_SYMBOL_CACHE, NULL); } -static int -sysprof_symbol_cache_compare (gconstpointer a, - gconstpointer b, - gpointer user_data) +#if 0 +static void +print_tree (SysprofSymbolCacheNode *node) { - const SysprofSymbol *sym_a = a; - const SysprofSymbol *sym_b = b; + SysprofSymbolCacheNode *left = RB_LEFT (node, link); + SysprofSymbolCacheNode *right = RB_RIGHT (node, link); - if (sym_a->begin_address < sym_b->begin_address) - return -1; + g_print ("[%lx:%lx max=%lx];\n", node->low, node->high, node->max); - if (sym_a->begin_address > sym_b->end_address) - return 1; + if (left) + { + g_print ("[%lx:%lx]'L -> [%lx:%lx];\n", + node->low, node->high, + left->low, left->high); + print_tree (left); + } - return 0; + if (right) + { + g_print ("[%lx:%lx]'R -> [%lx:%lx];\n", + node->low, node->high, + right->low, right->high); + print_tree (right); + } } +#endif -/** - * sysprof_symbol_cache_take: - * @self: a #SysprofSymbolCache - * @symbol: (transfer full): a #SysprofSymbol - * - */ void sysprof_symbol_cache_take (SysprofSymbolCache *self, SysprofSymbol *symbol) { + SysprofSymbolCacheNode *node; + SysprofSymbolCacheNode *parent; + g_return_if_fail (SYSPROF_IS_SYMBOL_CACHE (self)); g_return_if_fail (SYSPROF_IS_SYMBOL (symbol)); + g_return_if_fail (symbol->end_address > symbol->begin_address); - if (symbol->begin_address == 0 || symbol->end_address == 0) - return; + /* Some symbols are not suitable for our interval tree */ + if (symbol->begin_address == 0 || + symbol->end_address == 0 || + symbol->begin_address == symbol->end_address) + { + g_object_unref (symbol); + return; + } - g_sequence_insert_sorted (self->symbols, - g_object_ref (symbol), - sysprof_symbol_cache_compare, - NULL); -} + node = g_new0 (SysprofSymbolCacheNode, 1); + node->symbol = symbol; + node->low = symbol->begin_address; + node->high = symbol->end_address-1; + node->max = node->high; -static int -sysprof_symbol_cache_lookup_func (gconstpointer a, - gconstpointer b, - gpointer user_data) -{ - const SysprofSymbol *sym_a = a; - const gint64 *addr = b; + RB_INSERT(sysprof_symbol_cache, &self->head, node); - if (*addr < sym_a->begin_address) - return 1; + parent = RB_PARENT(node, link); - if (*addr > sym_a->end_address) - return -1; + while (parent != NULL) + { + if (node->max > parent->max) + parent->max = node->max; + node = parent; + parent = RB_PARENT(parent, link); + } - return 0; +#if 0 + g_print ("=====\n"); + print_tree (RB_ROOT (&self->head)); +#endif } SysprofSymbol * sysprof_symbol_cache_lookup (SysprofSymbolCache *self, SysprofAddress address) { - GSequenceIter *iter; + SysprofSymbolCacheNode *node; g_return_val_if_fail (SYSPROF_IS_SYMBOL_CACHE (self), NULL); if (address == 0) return NULL; - iter = g_sequence_lookup (self->symbols, - &address, - sysprof_symbol_cache_lookup_func, - NULL); + node = RB_ROOT(&self->head); - if (iter != NULL) - return g_sequence_get (iter); + while (node != NULL) + { + g_assert (RB_LEFT(node, link) == NULL || + node->max >= RB_LEFT(node, link)->max); + g_assert (RB_RIGHT(node, link) == NULL || + node->max >= RB_RIGHT(node, link)->max); + + if (address >= node->low && address <= node->high) + return node->symbol; + + if (RB_LEFT(node, link) && RB_LEFT(node, link)->max >= address) + node = RB_LEFT(node, link); + else + node = RB_RIGHT(node, link); + } return NULL; } diff --git a/src/libsysprof-analyze/tests/meson.build b/src/libsysprof-analyze/tests/meson.build index b1a045cc..1e161e3a 100644 --- a/src/libsysprof-analyze/tests/meson.build +++ b/src/libsysprof-analyze/tests/meson.build @@ -17,11 +17,11 @@ libsysprof_analyze_testsuite = { 'test-print-file' : {'skip': true}, 'test-list-processes' : {'skip': true}, 'test-symbolize' : {'skip': true}, + 'test-symbol-cache' : {}, } libsysprof_analyze_testsuite_deps = [ - libsysprof_analyze_dep, - libsysprof_capture_dep, + libsysprof_analyze_static_dep, ] foreach test, params: libsysprof_analyze_testsuite diff --git a/src/libsysprof-analyze/tests/test-symbol-cache.c b/src/libsysprof-analyze/tests/test-symbol-cache.c new file mode 100644 index 00000000..02956497 --- /dev/null +++ b/src/libsysprof-analyze/tests/test-symbol-cache.c @@ -0,0 +1,171 @@ +/* test-symbol-cache.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include + +#include + +#include "sysprof-symbol-private.h" +#include "sysprof-symbol-cache-private.h" + +typedef struct _SymbolInfo +{ + const char *name; + guint64 begin; + guint64 end; + int position; + int sort; + SysprofSymbol *symbol; +} SymbolInfo; + +static SysprofSymbol * +create_symbol (const char *name, + guint64 begin, + guint64 end) +{ + g_assert (begin < end); + + return _sysprof_symbol_new (g_ref_string_new (name), NULL, NULL, begin, end); +} + +static int +sort_by_key (gconstpointer a, + gconstpointer b) +{ + const SymbolInfo *info_a = a; + const SymbolInfo *info_b = b; + + if (info_a->sort < info_b->sort) + return -1; + else if (info_a->sort > info_b->sort) + return 1; + else + return 0; +} + +static int +sort_by_position (gconstpointer a, + gconstpointer b) +{ + const SymbolInfo *info_a = a; + const SymbolInfo *info_b = b; + + if (info_a->position < info_b->position) + return -1; + else if (info_a->position > info_b->position) + return 1; + else + return 0; +} + +static void +test_interval_tree (void) +{ + SysprofSymbolCache *symbol_cache = sysprof_symbol_cache_new (); + SymbolInfo symbols[] = { + { "symbol1", 0x10000, 0x20000 }, + { "symbol2", 0x20000, 0x30000 }, + { "symbol3", 0x30000, 0x40000 }, + { "symbol4", 0x90000, 0xa0000 }, + { "symbol5", 0xb0000, 0xb0001 }, + { "symbol6", 0xb0001, 0xb0002 }, + }; + + /* Add some randomness on insertion */ + for (guint i = 0; i < G_N_ELEMENTS (symbols); i++) + { + symbols[i].position = i; + symbols[i].sort = g_random_int (); + } + + /* Sort randomly for insertion */ + qsort (symbols, G_N_ELEMENTS (symbols), sizeof (SymbolInfo), sort_by_key); + for (guint i = 0; i < G_N_ELEMENTS (symbols); i++) + { + SymbolInfo *info = &symbols[i]; + + g_assert_cmpint (info->begin, <, info->end); + + info->symbol = create_symbol (info->name, info->begin, info->end); + + g_assert_nonnull (info->symbol); + g_assert_true (SYSPROF_IS_SYMBOL (info->symbol)); + + sysprof_symbol_cache_take (symbol_cache, g_object_ref (info->symbol)); + } + + /* Now resort to do lookups with edge checking */ + qsort (symbols, G_N_ELEMENTS (symbols), sizeof (SymbolInfo), sort_by_position); + for (guint i = 0; i < G_N_ELEMENTS (symbols); i++) + { + const SymbolInfo *info = &symbols[i]; + const SymbolInfo *prev = i > 0 ? &symbols[i-1] : NULL; + const SymbolInfo *next = i + 1 < G_N_ELEMENTS (symbols) ? &symbols[i+1] : NULL; + SysprofSymbol *lookup; + + g_assert_cmpint (info->position, ==, i); + + lookup = sysprof_symbol_cache_lookup (symbol_cache, info->begin-1); + if (prev && info->begin == prev->end) + g_assert_true (lookup == prev->symbol); + else + g_assert_null (lookup); + + lookup = sysprof_symbol_cache_lookup (symbol_cache, info->begin); + g_assert_nonnull (lookup); + g_assert_true (lookup == info->symbol); + + lookup = sysprof_symbol_cache_lookup (symbol_cache, info->end); + if (next == NULL || next->begin > info->end) + g_assert_null (lookup); + else + g_assert_true (lookup == next->symbol); + + if (info->begin+1 != info->end) + { + lookup = sysprof_symbol_cache_lookup (symbol_cache, info->begin+1); + g_assert_nonnull (lookup); + g_assert_true (lookup == info->symbol); + } + + lookup = sysprof_symbol_cache_lookup (symbol_cache, info->end-1); + g_assert_nonnull (lookup); + g_assert_true (lookup == info->symbol); + + lookup = sysprof_symbol_cache_lookup (symbol_cache, info->begin + ((info->end-info->begin)/2)); + g_assert_nonnull (lookup); + g_assert_true (lookup == info->symbol); + } + + g_assert_finalize_object (symbol_cache); + + for (guint i = 0; i < G_N_ELEMENTS (symbols); i++) + g_assert_finalize_object (symbols[i].symbol); +} + +int +main (int argc, + char *argv[]) +{ + g_test_init (&argc, &argv, NULL); + g_test_add_func ("/libsysprof-analyze/SysprofSymbolCache/interval-tree", + test_interval_tree); + return g_test_run (); +} From 59044bd813c43ce7eb87e3cd8c69cffdd43ab2c5 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 12 May 2023 14:15:18 -0700 Subject: [PATCH 0120/1030] libsysprof-analyze: remove implementation debugging code --- src/libsysprof-analyze/sysprof-symbol-cache.c | 32 ------------------- 1 file changed, 32 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-symbol-cache.c b/src/libsysprof-analyze/sysprof-symbol-cache.c index d4f7b866..3a8c5792 100644 --- a/src/libsysprof-analyze/sysprof-symbol-cache.c +++ b/src/libsysprof-analyze/sysprof-symbol-cache.c @@ -126,33 +126,6 @@ sysprof_symbol_cache_new (void) return g_object_new (SYSPROF_TYPE_SYMBOL_CACHE, NULL); } -#if 0 -static void -print_tree (SysprofSymbolCacheNode *node) -{ - SysprofSymbolCacheNode *left = RB_LEFT (node, link); - SysprofSymbolCacheNode *right = RB_RIGHT (node, link); - - g_print ("[%lx:%lx max=%lx];\n", node->low, node->high, node->max); - - if (left) - { - g_print ("[%lx:%lx]'L -> [%lx:%lx];\n", - node->low, node->high, - left->low, left->high); - print_tree (left); - } - - if (right) - { - g_print ("[%lx:%lx]'R -> [%lx:%lx];\n", - node->low, node->high, - right->low, right->high); - print_tree (right); - } -} -#endif - void sysprof_symbol_cache_take (SysprofSymbolCache *self, SysprofSymbol *symbol) @@ -190,11 +163,6 @@ sysprof_symbol_cache_take (SysprofSymbolCache *self, node = parent; parent = RB_PARENT(parent, link); } - -#if 0 - g_print ("=====\n"); - print_tree (RB_ROOT (&self->head)); -#endif } SysprofSymbol * From 9f6e16e3736cd6374a59568a5a6e5600aeeb485a Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 12 May 2023 14:29:16 -0700 Subject: [PATCH 0121/1030] libsysprof-analyze: take depth from translation func --- src/libsysprof-analyze/tests/test-symbolize.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/libsysprof-analyze/tests/test-symbolize.c b/src/libsysprof-analyze/tests/test-symbolize.c index 3f4223a5..5f553cd4 100644 --- a/src/libsysprof-analyze/tests/test-symbolize.c +++ b/src/libsysprof-analyze/tests/test-symbolize.c @@ -36,15 +36,11 @@ symbolize_cb (GObject *object, g_assert (SYSPROF_IS_DOCUMENT_TRACEABLE (traceable)); last_context = SYSPROF_ADDRESS_CONTEXT_NONE; - depth = sysprof_document_traceable_get_stack_depth (traceable); pid = sysprof_document_frame_get_pid (SYSPROF_DOCUMENT_FRAME (traceable)); + depth = sysprof_document_traceable_get_stack_addresses (traceable, addresses, G_N_ELEMENTS (addresses)); g_print ("%s depth=%u pid=%u\n", - G_OBJECT_TYPE_NAME (traceable), - depth, - pid); - - sysprof_document_traceable_get_stack_addresses (traceable, addresses, G_N_ELEMENTS (addresses)); + G_OBJECT_TYPE_NAME (traceable), depth, pid); for (guint j = 0; j < depth; j++) { From 3350ad61eb8ede7d797e06d3a4fdb6f8a5a6db32 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 12 May 2023 15:38:16 -0700 Subject: [PATCH 0122/1030] libsysprof-analyze: introduce SysprofDocumentLoader and thereby make a bunch of the exposed API on SysprofDocument private. Instead we'll push some of that to the loader but for now the tests can keep doing what their doing using the private API. The goal here is to not expose a SysprofDocument pointer until the document has been loaded and symbolized via the loader API. Then we can lookup symbols directly from the document w/o intermediary objects. --- src/libsysprof-analyze/meson.build | 2 + src/libsysprof-analyze/sysprof-analyze.h | 1 + .../sysprof-document-loader.c | 423 ++++++++++++++++++ .../sysprof-document-loader.h | 65 +++ .../sysprof-document-private.h | 20 +- src/libsysprof-analyze/sysprof-document.c | 32 +- src/libsysprof-analyze/sysprof-document.h | 16 - .../tests/test-capture-model.c | 5 +- .../tests/test-list-files.c | 4 +- .../tests/test-list-processes.c | 4 +- .../tests/test-print-file.c | 4 +- src/libsysprof-analyze/tests/test-symbolize.c | 16 +- 12 files changed, 543 insertions(+), 49 deletions(-) create mode 100644 src/libsysprof-analyze/sysprof-document-loader.c create mode 100644 src/libsysprof-analyze/sysprof-document-loader.h diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build index 142b21fc..4edb988a 100644 --- a/src/libsysprof-analyze/meson.build +++ b/src/libsysprof-analyze/meson.build @@ -7,6 +7,7 @@ libsysprof_analyze_public_sources = [ 'sysprof-document-file-chunk.c', 'sysprof-document-fork.c', 'sysprof-document-frame.c', + 'sysprof-document-loader.c', 'sysprof-document-log.c', 'sysprof-document-mark.c', 'sysprof-document-metadata.c', @@ -41,6 +42,7 @@ libsysprof_analyze_public_headers = [ 'sysprof-document-file-chunk.h', 'sysprof-document-fork.h', 'sysprof-document-frame.h', + 'sysprof-document-loader.h', 'sysprof-document-log.h', 'sysprof-document-mark.h', 'sysprof-document-metadata.h', diff --git a/src/libsysprof-analyze/sysprof-analyze.h b/src/libsysprof-analyze/sysprof-analyze.h index b47f3d54..a72e193a 100644 --- a/src/libsysprof-analyze/sysprof-analyze.h +++ b/src/libsysprof-analyze/sysprof-analyze.h @@ -33,6 +33,7 @@ G_BEGIN_DECLS # include "sysprof-document-file-chunk.h" # include "sysprof-document-fork.h" # include "sysprof-document-frame.h" +# include "sysprof-document-loader.h" # include "sysprof-document-log.h" # include "sysprof-document-mark.h" # include "sysprof-document-metadata.h" diff --git a/src/libsysprof-analyze/sysprof-document-loader.c b/src/libsysprof-analyze/sysprof-document-loader.c new file mode 100644 index 00000000..7d4fc8fe --- /dev/null +++ b/src/libsysprof-analyze/sysprof-document-loader.c @@ -0,0 +1,423 @@ +/* sysprof-document-loader.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-document-loader.h" +#include "sysprof-document-private.h" + +struct _SysprofDocumentLoader +{ + GObject parent_instance; + SysprofSymbolizer *symbolizer; + char *filename; + char *message; + double fraction; + int fd; +}; + +enum { + PROP_0, + PROP_FRACTION, + PROP_MESSAGE, + PROP_SYMBOLIZER, + N_PROPS +}; + +G_DEFINE_FINAL_TYPE (SysprofDocumentLoader, sysprof_document_loader, G_TYPE_OBJECT) + +static GParamSpec *properties [N_PROPS]; + +static void +sysprof_document_loader_finalize (GObject *object) +{ + SysprofDocumentLoader *self = (SysprofDocumentLoader *)object; + + g_clear_object (&self->symbolizer); + g_clear_pointer (&self->filename, g_free); + g_clear_pointer (&self->message, g_free); + + if (self->fd != -1) + { + close (self->fd); + self->fd = -1; + } + + G_OBJECT_CLASS (sysprof_document_loader_parent_class)->finalize (object); +} + +static void +sysprof_document_loader_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofDocumentLoader *self = SYSPROF_DOCUMENT_LOADER (object); + + switch (prop_id) + { + case PROP_FRACTION: + g_value_set_double (value, sysprof_document_loader_get_fraction (self)); + break; + + case PROP_MESSAGE: + g_value_set_string (value, sysprof_document_loader_get_message (self)); + break; + + case PROP_SYMBOLIZER: + g_value_set_object (value, sysprof_document_loader_get_symbolizer (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_document_loader_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SysprofDocumentLoader *self = SYSPROF_DOCUMENT_LOADER (object); + + switch (prop_id) + { + case PROP_SYMBOLIZER: + sysprof_document_loader_set_symbolizer (self, g_value_get_object (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_document_loader_class_init (SysprofDocumentLoaderClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + 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; + + properties [PROP_FRACTION] = + g_param_spec_double ("fraction", NULL, NULL, + 0, 1, 0, + (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_SYMBOLIZER] = + g_param_spec_object ("symbolizer", NULL, NULL, + SYSPROF_TYPE_SYMBOLIZER, + (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); +} + +static void +sysprof_document_loader_init (SysprofDocumentLoader *self) +{ + self->fd = -1; +} + +SysprofDocumentLoader * +sysprof_document_loader_new (const char *filename) +{ + SysprofDocumentLoader *self; + + g_return_val_if_fail (filename != NULL, NULL); + + self = g_object_new (SYSPROF_TYPE_DOCUMENT_LOADER, NULL); + self->filename = g_strdup (filename); + + return self; +} + +SysprofDocumentLoader * +sysprof_document_loader_new_for_fd (int fd, + GError **error) +{ + g_autoptr(SysprofDocumentLoader) self = NULL; + + self = g_object_new (SYSPROF_TYPE_SYMBOLIZER, NULL); + + if (-1 == (self->fd = dup (fd))) + { + int errsv = errno; + g_set_error_literal (error, + G_IO_ERROR, + g_io_error_from_errno (errsv), + g_strerror (errsv)); + return NULL; + } + + return g_steal_pointer (&self); +} + +/** + * sysprof_document_loader_get_symbolizer: + * @self: a #SysprofDocumentLoader + * + * Gets the #SysprofSymbolizer to use to symbolize traces within + * the document. + * + * Returns: (transfer none) (nullable): a #SysprofSymbolizer or %NULL + */ +SysprofSymbolizer * +sysprof_document_loader_get_symbolizer (SysprofDocumentLoader *self) +{ + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_LOADER (self), NULL); + + return self->symbolizer; +} + +/** + * sysprof_document_loader_set_symbolizer: + * @self: a #SysprofDocumentLoader + * @symbolizer: (nullable): a #SysprofSymbolizer or %NULL + * + * Sets the symbolizer to use to convert instruction pointers to + * symbol names in the document. + * + * If set to %NULL, a sensible default will be chosen when loading. + */ +void +sysprof_document_loader_set_symbolizer (SysprofDocumentLoader *self, + SysprofSymbolizer *symbolizer) +{ + g_return_if_fail (SYSPROF_IS_DOCUMENT_LOADER (self)); + + if (g_set_object (&self->symbolizer, symbolizer)) + g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_SYMBOLIZER]); +} + +/** + * sysprof_document_loader_get_fraction: + * @self: a #SysprofDocumentLoader + * + * Gets the fraction between 0 and 1 for the loading that has occurred. + * + * Returns: A value between 0 and 1. + */ +double +sysprof_document_loader_get_fraction (SysprofDocumentLoader *self) +{ + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_LOADER (self), .0); + + return self->fraction; +} + +/** + * sysprof_document_loader_get_message: + * @self: a #SysprofDocumentLoader + * + * Gets a text message representing what is happenin with loading. + * + * This only updates between calls of sysprof_document_loader_load_async() + * and sysprof_document_loader_load_finish(). + * + * Returns: (nullable): a string containing a load message + */ +const char * +sysprof_document_loader_get_message (SysprofDocumentLoader *self) +{ + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_LOADER (self), NULL); + + return self->message; +} + +static void +sysprof_document_loader_load_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + SysprofDocument *document = (SysprofDocument *)object; + g_autoptr(SysprofDocumentSymbols) symbols = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GTask) task = user_data; + + g_assert (SYSPROF_IS_DOCUMENT (document)); + g_assert (G_IS_ASYNC_RESULT (result)); + g_assert (G_IS_TASK (task)); + + if (!(symbols = _sysprof_document_symbolize_finish (document, result, &error))) + g_task_return_error (task, g_steal_pointer (&error)); + else + g_task_return_pointer (task, g_object_ref (document), g_object_unref); +} + +/** + * sysprof_document_loader_load_async: + * @self: a #SysprofDocumentLoader + * @cancellable: (nullable): a #GCancellable or %NULL + * @callback: a callback to execute upon completion + * @user_data: closure data for @callback + * + * Asynchronously loads the document. + * + * @callback should call sysprof_document_loader_load_finish(). + */ +void +sysprof_document_loader_load_async (SysprofDocumentLoader *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_autoptr(SysprofDocument) document = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GTask) task = NULL; + + g_return_if_fail (SYSPROF_IS_DOCUMENT_LOADER (self)); + g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); + + task = g_task_new (self, cancellable, callback, user_data); + g_task_set_source_tag (task, sysprof_document_loader_load_async); + + if (self->fd == -1 && self->filename == NULL) + { + g_task_return_new_error (task, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "SysprofDocumentLoader disposition must have fd or filename set"); + return; + } + + if (self->fd != -1) + document = _sysprof_document_new_from_fd (self->fd, &error); + else if (self->filename != NULL) + document = _sysprof_document_new (self->filename, &error); + else + g_assert_not_reached (); + + /* TODO: This will probably get renamed to load_async as we want + * to always have a symbolizer for loading. Additionally, the + * document will deal with caches directly instead of the symbols + * object. + */ + + _sysprof_document_symbolize_async (document, + self->symbolizer, + cancellable, + sysprof_document_loader_load_cb, + g_steal_pointer (&task)); +} + +/** + * sysprof_document_loader_load_finish: + * @self: a #SysprofDocumentLoader + * @result: a #GAsyncResult + * @error: a location for a #GError, or %NULL + * + * Completes a request to load a document asynchronously. + * + * Returns: (transfer full): a #SysprofDocumentLoader or %NULL + * and @error is set. + */ +SysprofDocument * +sysprof_document_loader_load_finish (SysprofDocumentLoader *self, + GAsyncResult *result, + GError **error) +{ + SysprofDocument *ret; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_LOADER (self), NULL); + g_return_val_if_fail (G_IS_TASK (result), NULL); + g_return_val_if_fail (g_task_is_valid (result, self), NULL); + + ret = g_task_propagate_pointer (G_TASK (result), error); + + g_return_val_if_fail (!ret || SYSPROF_IS_DOCUMENT (ret), NULL); + + return ret; +} + +typedef struct SysprofDocumentLoaderSync +{ + GMainContext *main_context; + SysprofDocument *document; + GError *error; +} SysprofDocumentLoaderSync; + +static void +sysprof_document_loader_load_sync_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + SysprofDocumentLoader *loader = (SysprofDocumentLoader *)object; + SysprofDocumentLoaderSync *state = user_data; + + g_assert (SYSPROF_IS_DOCUMENT_LOADER (loader)); + g_assert (G_IS_ASYNC_RESULT (result)); + g_assert (state != NULL); + g_assert (state->main_context != NULL); + + state->document = sysprof_document_loader_load_finish (loader, result, &state->error); + + g_main_context_wakeup (state->main_context); +} + +/** + * sysprof_document_loader_load: + * @self: a #SysprofDocumentLoader + * @cancellable: (nullable): a #GCancellable or %NULL + * @error: a location for a #GError, or %NULL + * + * Synchronously loads the document. + * + * This function requires a #GMainContext to be set for the current + * thread and uses the asynchronously loader API underneath. + * + * Returns: (transfer full): a #SysprofDocument if successful; otherwise + * %NULL and @error is set. + */ +SysprofDocument * +sysprof_document_loader_load (SysprofDocumentLoader *self, + GCancellable *cancellable, + GError **error) +{ + SysprofDocumentLoaderSync state; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_LOADER (self), NULL); + g_return_val_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable), NULL); + + state.main_context = g_main_context_ref_thread_default (); + state.document = NULL; + state.error = NULL; + + sysprof_document_loader_load_async (self, + cancellable, + sysprof_document_loader_load_sync_cb, + &state); + + while (state.document == NULL && state.error == NULL) + g_main_context_iteration (state.main_context, TRUE); + + g_main_context_unref (state.main_context); + + if (state.error != NULL) + g_propagate_error (error, state.error); + + return state.document; +} diff --git a/src/libsysprof-analyze/sysprof-document-loader.h b/src/libsysprof-analyze/sysprof-document-loader.h new file mode 100644 index 00000000..289dae30 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-document-loader.h @@ -0,0 +1,65 @@ +/* sysprof-document-loader.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +#include + +#include "sysprof-document.h" +#include "sysprof-symbolizer.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_DOCUMENT_LOADER (sysprof_document_loader_get_type()) + +SYSPROF_AVAILABLE_IN_ALL +G_DECLARE_FINAL_TYPE (SysprofDocumentLoader, sysprof_document_loader, SYSPROF, DOCUMENT_LOADER, GObject) + +SYSPROF_AVAILABLE_IN_ALL +SysprofDocumentLoader *sysprof_document_loader_new (const char *filename); +SYSPROF_AVAILABLE_IN_ALL +SysprofDocumentLoader *sysprof_document_loader_new_for_fd (int fd, + GError **error); +SYSPROF_AVAILABLE_IN_ALL +SysprofSymbolizer *sysprof_document_loader_get_symbolizer (SysprofDocumentLoader *self); +SYSPROF_AVAILABLE_IN_ALL +void sysprof_document_loader_set_symbolizer (SysprofDocumentLoader *self, + SysprofSymbolizer *symbolizer); +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_ALL +SysprofDocument *sysprof_document_loader_load (SysprofDocumentLoader *self, + GCancellable *cancellable, + GError **error); +SYSPROF_AVAILABLE_IN_ALL +void sysprof_document_loader_load_async (SysprofDocumentLoader *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +SYSPROF_AVAILABLE_IN_ALL +SysprofDocument *sysprof_document_loader_load_finish (SysprofDocumentLoader *self, + GAsyncResult *result, + GError **error); + +G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-document-private.h b/src/libsysprof-analyze/sysprof-document-private.h index 01ab04c8..5993e6ea 100644 --- a/src/libsysprof-analyze/sysprof-document-private.h +++ b/src/libsysprof-analyze/sysprof-document-private.h @@ -26,9 +26,21 @@ G_BEGIN_DECLS -gboolean _sysprof_document_is_native (SysprofDocument *self); -char *_sysprof_document_ref_string (SysprofDocument *self, - const char *name); -GtkBitset *_sysprof_document_traceables (SysprofDocument *self); +SysprofDocument *_sysprof_document_new (const char *filename, + GError **error); +SysprofDocument *_sysprof_document_new_from_fd (int capture_fd, + GError **error); +void _sysprof_document_symbolize_async (SysprofDocument *self, + SysprofSymbolizer *symbolizer, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +SysprofDocumentSymbols *_sysprof_document_symbolize_finish (SysprofDocument *self, + GAsyncResult *result, + GError **error); +gboolean _sysprof_document_is_native (SysprofDocument *self); +char *_sysprof_document_ref_string (SysprofDocument *self, + const char *name); +GtkBitset *_sysprof_document_traceables (SysprofDocument *self); G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index 788f2aab..0449944a 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -483,7 +483,7 @@ sysprof_document_load (SysprofDocument *self, } /** - * sysprof_document_new_from_fd: + * _sysprof_document_new_from_fd: * @capture_fd: a file-descriptor to be mapped * @error: a location for a #GError, or %NULL * @@ -495,12 +495,10 @@ sysprof_document_load (SysprofDocument *self, * * Returns: A #SysprofDocument if successful; otherwise %NULL * and @error is set. - * - * Since: 45.0 */ SysprofDocument * -sysprof_document_new_from_fd (int capture_fd, - GError **error) +_sysprof_document_new_from_fd (int capture_fd, + GError **error) { g_autoptr(SysprofDocument) self = NULL; @@ -528,8 +526,8 @@ sysprof_document_new_from_fd (int capture_fd, * Since: 45.0 */ SysprofDocument * -sysprof_document_new (const char *filename, - GError **error) +_sysprof_document_new (const char *filename, + GError **error) { g_autoptr(SysprofDocument) self = NULL; g_autofd int capture_fd = -1; @@ -614,11 +612,11 @@ sysprof_document_symbolize_prepare_cb (GObject *object, } void -sysprof_document_symbolize_async (SysprofDocument *self, - SysprofSymbolizer *symbolizer, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) +_sysprof_document_symbolize_async (SysprofDocument *self, + SysprofSymbolizer *symbolizer, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { g_autoptr(SysprofDocumentSymbols) symbols = NULL; g_autoptr(GTask) task = NULL; @@ -628,7 +626,7 @@ sysprof_document_symbolize_async (SysprofDocument *self, g_return_if_fail (!cancellable || SYSPROF_IS_SYMBOLIZER (symbolizer)); task = g_task_new (self, cancellable, callback, user_data); - g_task_set_source_tag (task, sysprof_document_symbolize_async); + g_task_set_source_tag (task, _sysprof_document_symbolize_async); _sysprof_symbolizer_prepare_async (symbolizer, self, @@ -638,14 +636,14 @@ sysprof_document_symbolize_async (SysprofDocument *self, } SysprofDocumentSymbols * -sysprof_document_symbolize_finish (SysprofDocument *self, - GAsyncResult *result, - GError **error) +_sysprof_document_symbolize_finish (SysprofDocument *self, + GAsyncResult *result, + GError **error) { g_return_val_if_fail (SYSPROF_IS_DOCUMENT (self), NULL); g_return_val_if_fail (G_IS_TASK (result), NULL); g_return_val_if_fail (g_task_is_valid (result, self), NULL); - g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == sysprof_document_symbolize_async, NULL); + g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == _sysprof_document_symbolize_async, NULL); return g_task_propagate_pointer (G_TASK (result), error); } diff --git a/src/libsysprof-analyze/sysprof-document.h b/src/libsysprof-analyze/sysprof-document.h index 0fc3d08e..aed68f10 100644 --- a/src/libsysprof-analyze/sysprof-document.h +++ b/src/libsysprof-analyze/sysprof-document.h @@ -35,22 +35,6 @@ G_BEGIN_DECLS SYSPROF_AVAILABLE_IN_ALL G_DECLARE_FINAL_TYPE (SysprofDocument, sysprof_document, SYSPROF, DOCUMENT, GObject) -SYSPROF_AVAILABLE_IN_ALL -SysprofDocument *sysprof_document_new (const char *filename, - GError **error); -SYSPROF_AVAILABLE_IN_ALL -SysprofDocument *sysprof_document_new_from_fd (int capture_fd, - GError **error); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_document_symbolize_async (SysprofDocument *self, - SysprofSymbolizer *symbolizer, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -SYSPROF_AVAILABLE_IN_ALL -SysprofDocumentSymbols *sysprof_document_symbolize_finish (SysprofDocument *self, - GAsyncResult *result, - GError **error); SYSPROF_AVAILABLE_IN_ALL SysprofDocumentFile *sysprof_document_lookup_file (SysprofDocument *self, const char *path); diff --git a/src/libsysprof-analyze/tests/test-capture-model.c b/src/libsysprof-analyze/tests/test-capture-model.c index 1e945528..2e16ee44 100644 --- a/src/libsysprof-analyze/tests/test-capture-model.c +++ b/src/libsysprof-analyze/tests/test-capture-model.c @@ -2,7 +2,8 @@ #include #include -#include + +#include "sysprof-document-private.h" int main (int argc, @@ -23,7 +24,7 @@ main (int argc, filename = argv[1]; - if (!(document = sysprof_document_new (filename, &error))) + if (!(document = _sysprof_document_new (filename, &error))) { g_printerr ("Failed to load %s: %s\n", filename, error->message); diff --git a/src/libsysprof-analyze/tests/test-list-files.c b/src/libsysprof-analyze/tests/test-list-files.c index bd2e177c..a79c4a2d 100644 --- a/src/libsysprof-analyze/tests/test-list-files.c +++ b/src/libsysprof-analyze/tests/test-list-files.c @@ -20,6 +20,8 @@ #include +#include "sysprof-document-private.h" + int main (int argc, char *argv[]) @@ -35,7 +37,7 @@ main (int argc, return 1; } - if (!(document = sysprof_document_new (argv[1], &error))) + if (!(document = _sysprof_document_new (argv[1], &error))) { g_printerr ("Failed to open capture: %s\n", error->message); return 1; diff --git a/src/libsysprof-analyze/tests/test-list-processes.c b/src/libsysprof-analyze/tests/test-list-processes.c index 7b1c8a45..d3d419da 100644 --- a/src/libsysprof-analyze/tests/test-list-processes.c +++ b/src/libsysprof-analyze/tests/test-list-processes.c @@ -20,6 +20,8 @@ #include +#include "sysprof-document-private.h" + int main (int argc, char *argv[]) @@ -35,7 +37,7 @@ main (int argc, return 1; } - if (!(document = sysprof_document_new (argv[1], &error))) + if (!(document = _sysprof_document_new (argv[1], &error))) { g_printerr ("Failed to open capture: %s\n", error->message); return 1; diff --git a/src/libsysprof-analyze/tests/test-print-file.c b/src/libsysprof-analyze/tests/test-print-file.c index ec593088..cd8d3720 100644 --- a/src/libsysprof-analyze/tests/test-print-file.c +++ b/src/libsysprof-analyze/tests/test-print-file.c @@ -20,6 +20,8 @@ #include +#include "sysprof-document-private.h" + int main (int argc, char *argv[]) @@ -36,7 +38,7 @@ main (int argc, return 1; } - if (!(document = sysprof_document_new (argv[1], &error))) + if (!(document = _sysprof_document_new (argv[1], &error))) { g_printerr ("Failed to open capture: %s\n", error->message); return 1; diff --git a/src/libsysprof-analyze/tests/test-symbolize.c b/src/libsysprof-analyze/tests/test-symbolize.c index 5f553cd4..8265d2da 100644 --- a/src/libsysprof-analyze/tests/test-symbolize.c +++ b/src/libsysprof-analyze/tests/test-symbolize.c @@ -2,6 +2,8 @@ #include +#include "sysprof-document-private.h" + static GMainLoop *main_loop; static void @@ -19,7 +21,7 @@ symbolize_cb (GObject *object, g_assert (SYSPROF_IS_DOCUMENT (document)); g_assert (G_IS_ASYNC_RESULT (result)); - if (!(symbols = sysprof_document_symbolize_finish (document, result, &error))) + if (!(symbols = _sysprof_document_symbolize_finish (document, result, &error))) g_error ("Failed to symbolize: %s", error->message); traceables = sysprof_document_list_traceables (document); @@ -88,7 +90,7 @@ main (int argc, filename = argv[1]; - if (!(document = sysprof_document_new (filename, &error))) + if (!(document = _sysprof_document_new (filename, &error))) { g_printerr ("Failed to load document: %s\n", error->message); return 1; @@ -97,11 +99,11 @@ main (int argc, multi = sysprof_multi_symbolizer_new (); sysprof_multi_symbolizer_add (multi, sysprof_bundled_symbolizer_new ()); - sysprof_document_symbolize_async (document, - SYSPROF_SYMBOLIZER (multi), - NULL, - symbolize_cb, - NULL); + _sysprof_document_symbolize_async (document, + SYSPROF_SYMBOLIZER (multi), + NULL, + symbolize_cb, + NULL); g_main_loop_run (main_loop); From 6429768373195ffc99c1a2b287f13171948c80a6 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Sun, 14 May 2023 18:19:30 -0700 Subject: [PATCH 0123/1030] libsysprof-analyze: add API to open input stream --- .../sysprof-document-file.c | 40 +++++++++++++++++++ .../sysprof-document-file.h | 8 ++-- 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-document-file.c b/src/libsysprof-analyze/sysprof-document-file.c index 56e7a00b..a3f33e8e 100644 --- a/src/libsysprof-analyze/sysprof-document-file.c +++ b/src/libsysprof-analyze/sysprof-document-file.c @@ -22,6 +22,7 @@ #include "sysprof-document-file-chunk.h" #include "sysprof-document-file-private.h" +#include "sysprof-document-frame-private.h" struct _SysprofDocumentFile { @@ -155,3 +156,42 @@ sysprof_document_file_dup_bytes (SysprofDocumentFile *self) return g_bytes_new_take (g_array_free (ar, FALSE), len); } + +/** + * sysprof_document_file_read: + * @self: a #SysprofDocumentFile + * + * Creates a new input stream containing the contents of the file + * within the document. + * + * Returns: (transfer full): a #GInputstream + */ +GInputStream * +sysprof_document_file_read (SysprofDocumentFile *self) +{ + GInputStream *input; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_FILE (self), NULL); + + input = g_memory_input_stream_new (); + + for (guint i = 0; i < self->file_chunks->len; i++) + { + g_autoptr(GBytes) bytes = NULL; + SysprofDocumentFileChunk *file_chunk; + const guint8 *data; + guint len; + + file_chunk = g_ptr_array_index (self->file_chunks, i); + data = sysprof_document_file_chunk_get_data (file_chunk, &len); + + bytes = g_bytes_new_with_free_func (data, + len, + (GDestroyNotify)g_mapped_file_unref, + g_mapped_file_ref (SYSPROF_DOCUMENT_FRAME (self)->mapped_file)); + + g_memory_input_stream_add_bytes (G_MEMORY_INPUT_STREAM (input), bytes); + } + + return g_steal_pointer (&input); +} diff --git a/src/libsysprof-analyze/sysprof-document-file.h b/src/libsysprof-analyze/sysprof-document-file.h index 0b66501d..3ca728dc 100644 --- a/src/libsysprof-analyze/sysprof-document-file.h +++ b/src/libsysprof-analyze/sysprof-document-file.h @@ -20,7 +20,7 @@ #pragma once -#include +#include #include @@ -32,8 +32,10 @@ SYSPROF_AVAILABLE_IN_ALL G_DECLARE_FINAL_TYPE (SysprofDocumentFile, sysprof_document_file, SYSPROF, DOCUMENT_FILE, GObject) SYSPROF_AVAILABLE_IN_ALL -const char *sysprof_document_file_get_path (SysprofDocumentFile *self); +const char *sysprof_document_file_get_path (SysprofDocumentFile *self); SYSPROF_AVAILABLE_IN_ALL -GBytes *sysprof_document_file_dup_bytes (SysprofDocumentFile *self); +GBytes *sysprof_document_file_dup_bytes (SysprofDocumentFile *self); +SYSPROF_AVAILABLE_IN_ALL +GInputStream *sysprof_document_file_read (SysprofDocumentFile *self); G_END_DECLS From 1bcb534e16f0559f50b4acb4a07fdf016ee79966 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Sun, 14 May 2023 18:36:29 -0700 Subject: [PATCH 0124/1030] libsysprof-analyze: add api to get size for file --- src/libsysprof-analyze/sysprof-document-file.c | 17 +++++++++++++++++ src/libsysprof-analyze/sysprof-document-file.h | 2 ++ 2 files changed, 19 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-document-file.c b/src/libsysprof-analyze/sysprof-document-file.c index a3f33e8e..fe7fd4cd 100644 --- a/src/libsysprof-analyze/sysprof-document-file.c +++ b/src/libsysprof-analyze/sysprof-document-file.c @@ -195,3 +195,20 @@ sysprof_document_file_read (SysprofDocumentFile *self) return g_steal_pointer (&input); } + +gsize +sysprof_document_file_get_size (SysprofDocumentFile *self) +{ + gsize size = 0; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_FILE (self), 0); + + for (guint i = 0; i < self->file_chunks->len; i++) + { + SysprofDocumentFileChunk *file_chunk = g_ptr_array_index (self->file_chunks, i); + + size += sysprof_document_file_chunk_get_size (file_chunk); + } + + return size; +} diff --git a/src/libsysprof-analyze/sysprof-document-file.h b/src/libsysprof-analyze/sysprof-document-file.h index 3ca728dc..4865e469 100644 --- a/src/libsysprof-analyze/sysprof-document-file.h +++ b/src/libsysprof-analyze/sysprof-document-file.h @@ -37,5 +37,7 @@ SYSPROF_AVAILABLE_IN_ALL GBytes *sysprof_document_file_dup_bytes (SysprofDocumentFile *self); SYSPROF_AVAILABLE_IN_ALL GInputStream *sysprof_document_file_read (SysprofDocumentFile *self); +SYSPROF_AVAILABLE_IN_ALL +gsize sysprof_document_file_get_size (SysprofDocumentFile *self); G_END_DECLS From a27f700c0683b9b9cca7c269e2f20a87e01cdd85 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Sun, 14 May 2023 18:45:03 -0700 Subject: [PATCH 0125/1030] libsysprof-analyze: add scaffolding for kallsyms parsing We will want to start embedding this content in the capture file (but after gzipping it as it's otherwise quite large). This will get things in place so that we can parse that .gz file into the address ranges and decode symbols found within the capture file. --- src/libsysprof-analyze/meson.build | 2 + src/libsysprof-analyze/sysprof-analyze.h | 1 + .../sysprof-kallsyms-symbolizer.c | 155 ++++++++++++++++++ .../sysprof-kallsyms-symbolizer.h | 42 +++++ 4 files changed, 200 insertions(+) create mode 100644 src/libsysprof-analyze/sysprof-kallsyms-symbolizer.c create mode 100644 src/libsysprof-analyze/sysprof-kallsyms-symbolizer.h diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build index 4edb988a..f19e88fe 100644 --- a/src/libsysprof-analyze/meson.build +++ b/src/libsysprof-analyze/meson.build @@ -16,6 +16,7 @@ libsysprof_analyze_public_sources = [ 'sysprof-document-sample.c', 'sysprof-document-symbols.c', 'sysprof-document-traceable.c', + 'sysprof-kallsyms-symbolizer.c', 'sysprof-multi-symbolizer.c', 'sysprof-symbol.c', 'sysprof-symbolizer.c', @@ -52,6 +53,7 @@ libsysprof_analyze_public_headers = [ 'sysprof-document-symbols.h', 'sysprof-document-traceable.h', 'sysprof-mount.h', + 'sysprof-kallsyms-symbolizer.h', 'sysprof-multi-symbolizer.h', 'sysprof-symbol.h', 'sysprof-symbolizer.h', diff --git a/src/libsysprof-analyze/sysprof-analyze.h b/src/libsysprof-analyze/sysprof-analyze.h index a72e193a..f81102e4 100644 --- a/src/libsysprof-analyze/sysprof-analyze.h +++ b/src/libsysprof-analyze/sysprof-analyze.h @@ -43,6 +43,7 @@ G_BEGIN_DECLS # include "sysprof-document-symbols.h" # include "sysprof-document-traceable.h" # include "sysprof-mount.h" +# include "sysprof-kallsyms-symbolizer.h" # include "sysprof-multi-symbolizer.h" # include "sysprof-symbol.h" # include "sysprof-symbolizer.h" diff --git a/src/libsysprof-analyze/sysprof-kallsyms-symbolizer.c b/src/libsysprof-analyze/sysprof-kallsyms-symbolizer.c new file mode 100644 index 00000000..e2d6dd4f --- /dev/null +++ b/src/libsysprof-analyze/sysprof-kallsyms-symbolizer.c @@ -0,0 +1,155 @@ +/* sysprof-kallsyms-symbolizer.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-kallsyms-symbolizer.h" +#include "sysprof-document-private.h" +#include "sysprof-symbolizer-private.h" +#include "sysprof-symbol-private.h" + +struct _SysprofKallsymsSymbolizer +{ + SysprofSymbolizer parent_instance; +}; + +struct _SysprofKallsymsSymbolizerClass +{ + SysprofSymbolizerClass parent_class; +}; + +G_DEFINE_FINAL_TYPE (SysprofKallsymsSymbolizer, sysprof_kallsyms_symbolizer, SYSPROF_TYPE_SYMBOLIZER) + +static void +sysprof_kallsyms_symbolizer_prepare_worker (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) +{ + SysprofKallsymsSymbolizer *self = source_object; + GDataInputStream *input = task_data; + g_autoptr(GError) error = NULL; + char *line; + gsize len; + + g_assert (G_IS_TASK (task)); + g_assert (SYSPROF_IS_KALLSYMS_SYMBOLIZER (self)); + g_assert (G_IS_DATA_INPUT_STREAM (input)); + + while ((line = g_data_input_stream_read_line_utf8 (input, &len, cancellable, &error))) + { + /* TODO: port kallsym parser from sysprof-kallsyms.c */ + + g_free (line); + } + + g_task_return_boolean (task, TRUE); +} + +static void +sysprof_kallsyms_symbolizer_prepare_async (SysprofSymbolizer *symbolizer, + SysprofDocument *document, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + SysprofKallsymsSymbolizer *self = (SysprofKallsymsSymbolizer *)symbolizer; + g_autoptr(SysprofDocumentFile) file = NULL; + g_autoptr(GZlibDecompressor) decompressor = NULL; + g_autoptr(GInputStream) input_gz = NULL; + g_autoptr(GInputStream) input = NULL; + g_autoptr(GTask) task = NULL; + + g_assert (SYSPROF_IS_KALLSYMS_SYMBOLIZER (self)); + g_assert (SYSPROF_IS_DOCUMENT (document)); + g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); + + task = g_task_new (self, cancellable, callback, user_data); + g_task_set_source_tag (task, sysprof_kallsyms_symbolizer_prepare_async); + + if (!(file = sysprof_document_lookup_file (document, "/proc/kallsyms.gz"))) + { + g_task_return_new_error (task, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "No kallsyms found to decode"); + return; + } + + decompressor = g_zlib_decompressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP); + input_gz = sysprof_document_file_read (file); + input = g_converter_input_stream_new (input_gz, G_CONVERTER (decompressor)); + + g_task_set_task_data (task, + g_data_input_stream_new (input), + g_object_unref); + g_task_run_in_thread (task, sysprof_kallsyms_symbolizer_prepare_worker); +} + +static gboolean +sysprof_kallsyms_symbolizer_prepare_finish (SysprofSymbolizer *symbolizer, + GAsyncResult *result, + GError **error) +{ + g_assert (SYSPROF_IS_KALLSYMS_SYMBOLIZER (symbolizer)); + g_assert (G_IS_TASK (result)); + g_assert (g_task_is_valid (result, symbolizer)); + + return g_task_propagate_boolean (G_TASK (result), error); +} + +static SysprofSymbol * +sysprof_kallsyms_symbolizer_symbolize (SysprofSymbolizer *symbolizer, + SysprofStrings *strings, + const SysprofProcessInfo *process_info, + SysprofAddress address) +{ + return NULL; +} + +static void +sysprof_kallsyms_symbolizer_finalize (GObject *object) +{ + G_OBJECT_CLASS (sysprof_kallsyms_symbolizer_parent_class)->finalize (object); +} + +static void +sysprof_kallsyms_symbolizer_class_init (SysprofKallsymsSymbolizerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + SysprofSymbolizerClass *symbolizer_class = SYSPROF_SYMBOLIZER_CLASS (klass); + + object_class->finalize = sysprof_kallsyms_symbolizer_finalize; + + symbolizer_class->prepare_async = sysprof_kallsyms_symbolizer_prepare_async; + symbolizer_class->prepare_finish = sysprof_kallsyms_symbolizer_prepare_finish; + symbolizer_class->symbolize = sysprof_kallsyms_symbolizer_symbolize; +} + +static void +sysprof_kallsyms_symbolizer_init (SysprofKallsymsSymbolizer *self) +{ +} + +SysprofSymbolizer * +sysprof_kallsyms_symbolizer_new (void) +{ + return g_object_new (SYSPROF_TYPE_KALLSYMS_SYMBOLIZER, NULL); +} diff --git a/src/libsysprof-analyze/sysprof-kallsyms-symbolizer.h b/src/libsysprof-analyze/sysprof-kallsyms-symbolizer.h new file mode 100644 index 00000000..9a766326 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-kallsyms-symbolizer.h @@ -0,0 +1,42 @@ +/* sysprof-kallsyms-symbolizer.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-symbolizer.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_KALLSYMS_SYMBOLIZER (sysprof_kallsyms_symbolizer_get_type()) +#define SYSPROF_IS_KALLSYMS_SYMBOLIZER(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_KALLSYMS_SYMBOLIZER) +#define SYSPROF_KALLSYMS_SYMBOLIZER(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_KALLSYMS_SYMBOLIZER, SysprofKallsymsSymbolizer) +#define SYSPROF_KALLSYMS_SYMBOLIZER_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_KALLSYMS_SYMBOLIZER, SysprofKallsymsSymbolizerClass) + +typedef struct _SysprofKallsymsSymbolizer SysprofKallsymsSymbolizer; +typedef struct _SysprofKallsymsSymbolizerClass SysprofKallsymsSymbolizerClass; + +SYSPROF_AVAILABLE_IN_ALL +GType sysprof_kallsyms_symbolizer_get_type (void) G_GNUC_CONST; +SYSPROF_AVAILABLE_IN_ALL +SysprofSymbolizer *sysprof_kallsyms_symbolizer_new (void); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofKallsymsSymbolizer, g_object_unref) + +G_END_DECLS From 0e95fd3841ed74c701214c7faa812abc0b60ee67 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 12 May 2023 16:22:44 -0700 Subject: [PATCH 0126/1030] libsysprof-analyze: add SysprofNoSymbolizer A symbolizer that will never symbolize. We can use this later internally to short-circuit various symoblization steps during loading. --- src/libsysprof-analyze/meson.build | 2 + src/libsysprof-analyze/sysprof-analyze.h | 1 + .../sysprof-no-symbolizer.c | 58 +++++++++++++++++++ .../sysprof-no-symbolizer.h | 37 ++++++++++++ 4 files changed, 98 insertions(+) create mode 100644 src/libsysprof-analyze/sysprof-no-symbolizer.c create mode 100644 src/libsysprof-analyze/sysprof-no-symbolizer.h diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build index f19e88fe..bd779c7f 100644 --- a/src/libsysprof-analyze/meson.build +++ b/src/libsysprof-analyze/meson.build @@ -18,6 +18,7 @@ libsysprof_analyze_public_sources = [ 'sysprof-document-traceable.c', 'sysprof-kallsyms-symbolizer.c', 'sysprof-multi-symbolizer.c', + 'sysprof-no-symbolizer.c', 'sysprof-symbol.c', 'sysprof-symbolizer.c', ] @@ -55,6 +56,7 @@ libsysprof_analyze_public_headers = [ 'sysprof-mount.h', 'sysprof-kallsyms-symbolizer.h', 'sysprof-multi-symbolizer.h', + 'sysprof-no-symbolizer.h', 'sysprof-symbol.h', 'sysprof-symbolizer.h', ] diff --git a/src/libsysprof-analyze/sysprof-analyze.h b/src/libsysprof-analyze/sysprof-analyze.h index f81102e4..f751127a 100644 --- a/src/libsysprof-analyze/sysprof-analyze.h +++ b/src/libsysprof-analyze/sysprof-analyze.h @@ -45,6 +45,7 @@ G_BEGIN_DECLS # include "sysprof-mount.h" # include "sysprof-kallsyms-symbolizer.h" # include "sysprof-multi-symbolizer.h" +# include "sysprof-no-symbolizer.h" # include "sysprof-symbol.h" # include "sysprof-symbolizer.h" #undef SYSPROF_ANALYZE_INSIDE diff --git a/src/libsysprof-analyze/sysprof-no-symbolizer.c b/src/libsysprof-analyze/sysprof-no-symbolizer.c new file mode 100644 index 00000000..9f803c5c --- /dev/null +++ b/src/libsysprof-analyze/sysprof-no-symbolizer.c @@ -0,0 +1,58 @@ +/* sysprof-no-symbolizer.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-no-symbolizer.h" + +struct _SysprofNoSymbolizer +{ + SysprofSymbolizer parent_instance; +}; + +G_DEFINE_FINAL_TYPE (SysprofNoSymbolizer, sysprof_no_symbolizer, SYSPROF_TYPE_SYMBOLIZER) + +static SysprofSymbol * +sysprof_no_symbolizer_symbolize (SysprofSymbolizer *symbolizer, + SysprofStrings *strings, + const SysprofProcessInfo *process_info, + SysprofAddress address) +{ + return NULL; +} + +static void +sysprof_no_symbolizer_class_init (SysprofNoSymbolizerClass *klass) +{ + SysprofSymbolizerClass *symbolizer_class = SYSPROF_SYMBOLIZER_CLASS (klass); + + symbolizer_class->symbolize = sysprof_no_symbolizer_symbolize; +} + +static void +sysprof_no_symbolizer_init (SysprofNoSymbolizer *self) +{ +} + +SysprofSymbolizer * +sysprof_no_symbolizer_new (void) +{ + return g_object_new (SYSPROF_TYPE_NO_SYMBOLIZER, NULL); +} diff --git a/src/libsysprof-analyze/sysprof-no-symbolizer.h b/src/libsysprof-analyze/sysprof-no-symbolizer.h new file mode 100644 index 00000000..ebe620b1 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-no-symbolizer.h @@ -0,0 +1,37 @@ +/* sysprof-no-symbolizer.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +#include "sysprof-symbolizer.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_NO_SYMBOLIZER (sysprof_no_symbolizer_get_type()) + +SYSPROF_AVAILABLE_IN_ALL +G_DECLARE_DERIVABLE_TYPE (SysprofNoSymbolizer, sysprof_no_symbolizer, SYSPROF, NO_SYMBOLIZER, SysprofSymbolizer) + +SYSPROF_AVAILABLE_IN_ALL +SysprofSymbolizer *sysprof_no_symbolizer_new (void); + +G_END_DECLS From ed030d2c25fdce6c3478b37594e5bf1285b0b308 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 12 May 2023 17:16:35 -0700 Subject: [PATCH 0127/1030] libsysprof-analyze: fix nick for symbol --- src/libsysprof-analyze/sysprof-bundled-symbolizer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libsysprof-analyze/sysprof-bundled-symbolizer.c b/src/libsysprof-analyze/sysprof-bundled-symbolizer.c index a4533dab..875a67a5 100644 --- a/src/libsysprof-analyze/sysprof-bundled-symbolizer.c +++ b/src/libsysprof-analyze/sysprof-bundled-symbolizer.c @@ -208,8 +208,8 @@ sysprof_bundled_symbolizer_symbolize (SysprofSymbolizer *symbolizer, if (ret->offset < (self->endptr - self->beginptr)) return _sysprof_symbol_new (sysprof_strings_get (strings, &self->beginptr[ret->offset]), - g_steal_pointer (&tag), NULL, + g_steal_pointer (&tag), ret->addr_begin, ret->addr_end); From 00ecc4120990a9bb6761550e958ee851cecd6b59 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 12 May 2023 17:20:10 -0700 Subject: [PATCH 0128/1030] libsysprof-analyze: make SysprofDocumentSymbols private This instead moves to a public API on the document to symbolize now that we've gotten much of the necessary bits private in loading the document. This commit ensures that we only do loading via the loader now (and removes the incorrect use from the tests so they too go through the loader). We check for NoSymbolizer in document symbols so that we can skip any decoding. That keeps various use cases fast where you don't want to waste time on symbolizing if you don't need to look at symbols. There is plenty more we can do to batch decode symbols with some more API changes, but that will come after we have kernel/userland decoding integrated from this library. We may still want to get all symbols into a single symbol cache, but given that we have address ranges associated with them, that may not be very useful beyond the hashtable to pid-specific cache we have now. If symbols were shared between processes, that'd make more sense, but we aren't doing that (albeit strings are shared between symbol instances to reduce that overhead). --- src/libsysprof-analyze/meson.build | 3 +- src/libsysprof-analyze/sysprof-analyze.h | 1 - .../sysprof-document-loader.c | 208 +++++++++++++++--- .../sysprof-document-private.h | 35 +-- .../sysprof-document-symbols-private.h | 29 ++- .../sysprof-document-symbols.c | 14 +- .../sysprof-document-symbols.h | 43 ---- src/libsysprof-analyze/sysprof-document.c | 196 ++++++++++------- src/libsysprof-analyze/sysprof-document.h | 19 +- .../sysprof-multi-symbolizer.c | 4 +- .../sysprof-multi-symbolizer.h | 2 +- .../sysprof-no-symbolizer.c | 22 +- .../sysprof-no-symbolizer.h | 17 +- .../tests/test-capture-model.c | 12 +- .../tests/test-list-files.c | 6 +- .../tests/test-list-processes.c | 6 +- .../tests/test-print-file.c | 6 +- src/libsysprof-analyze/tests/test-symbolize.c | 108 ++++----- 18 files changed, 463 insertions(+), 268 deletions(-) delete mode 100644 src/libsysprof-analyze/sysprof-document-symbols.h diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build index bd779c7f..ba11a216 100644 --- a/src/libsysprof-analyze/meson.build +++ b/src/libsysprof-analyze/meson.build @@ -14,7 +14,6 @@ libsysprof_analyze_public_sources = [ 'sysprof-document-mmap.c', 'sysprof-document-process.c', 'sysprof-document-sample.c', - 'sysprof-document-symbols.c', 'sysprof-document-traceable.c', 'sysprof-kallsyms-symbolizer.c', 'sysprof-multi-symbolizer.c', @@ -26,6 +25,7 @@ libsysprof_analyze_public_sources = [ libsysprof_analyze_private_sources = [ 'sysprof-address-layout.c', 'sysprof-document-bitset-index.c', + 'sysprof-document-symbols.c', 'sysprof-mount.c', 'sysprof-mount-device.c', 'sysprof-mount-namespace.c', @@ -51,7 +51,6 @@ libsysprof_analyze_public_headers = [ 'sysprof-document-mmap.h', 'sysprof-document-process.h', 'sysprof-document-sample.h', - 'sysprof-document-symbols.h', 'sysprof-document-traceable.h', 'sysprof-mount.h', 'sysprof-kallsyms-symbolizer.h', diff --git a/src/libsysprof-analyze/sysprof-analyze.h b/src/libsysprof-analyze/sysprof-analyze.h index f751127a..0461cd82 100644 --- a/src/libsysprof-analyze/sysprof-analyze.h +++ b/src/libsysprof-analyze/sysprof-analyze.h @@ -40,7 +40,6 @@ G_BEGIN_DECLS # include "sysprof-document-mmap.h" # include "sysprof-document-process.h" # include "sysprof-document-sample.h" -# include "sysprof-document-symbols.h" # include "sysprof-document-traceable.h" # include "sysprof-mount.h" # include "sysprof-kallsyms-symbolizer.h" diff --git a/src/libsysprof-analyze/sysprof-document-loader.c b/src/libsysprof-analyze/sysprof-document-loader.c index 7d4fc8fe..cfe65c52 100644 --- a/src/libsysprof-analyze/sysprof-document-loader.c +++ b/src/libsysprof-analyze/sysprof-document-loader.c @@ -20,8 +20,13 @@ #include "config.h" +#include +#include + +#include "sysprof-bundled-symbolizer.h" #include "sysprof-document-loader.h" #include "sysprof-document-private.h" +#include "sysprof-multi-symbolizer.h" struct _SysprofDocumentLoader { @@ -45,6 +50,105 @@ G_DEFINE_FINAL_TYPE (SysprofDocumentLoader, sysprof_document_loader, G_TYPE_OBJE static GParamSpec *properties [N_PROPS]; +static void +mapped_file_by_filename (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) +{ + g_autoptr(GError) error = NULL; + g_autoptr(GMappedFile) mapped_file = g_mapped_file_new (task_data, FALSE, &error); + + if (mapped_file != NULL) + g_task_return_pointer (task, + g_steal_pointer (&mapped_file), + (GDestroyNotify)g_mapped_file_unref); + else + g_task_return_error (task, g_steal_pointer (&error)); +} + +static void +mapped_file_new_async (const char *filename, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_autoptr(GTask) task = g_task_new (NULL, cancellable, callback, user_data); + g_task_set_task_data (task, g_strdup (filename), g_free); + g_task_run_in_thread (task, mapped_file_by_filename); +} + +static void +mapped_file_by_fd (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) +{ + g_autoptr(GError) error = NULL; + g_autoptr(GMappedFile) mapped_file = g_mapped_file_new_from_fd (GPOINTER_TO_INT (task_data), FALSE, &error); + + if (mapped_file != NULL) + g_task_return_pointer (task, + g_steal_pointer (&mapped_file), + (GDestroyNotify)g_mapped_file_unref); + else + g_task_return_error (task, g_steal_pointer (&error)); +} + +static void +close_fd (gpointer data) +{ + int fd = GPOINTER_TO_INT (data); + close (fd); +} + +static void +mapped_file_new_from_fd_async (int fd, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_autoptr(GTask) task = NULL; + int copy_fd; + + task = g_task_new (NULL, cancellable, callback, user_data); + + if (-1 == (copy_fd = dup (fd))) + { + int errsv = errno; + g_task_return_new_error (task, + G_IO_ERROR, + g_io_error_from_errno (errsv), + "%s", + g_strerror (errsv)); + return; + } + + g_task_set_task_data (task, GINT_TO_POINTER (copy_fd), close_fd); + g_task_run_in_thread (task, mapped_file_by_fd); +} + +static GMappedFile * +mapped_file_new_finish (GAsyncResult *result, + GError **error) +{ + return g_task_propagate_pointer (G_TASK (result), error); +} + +static void +set_default_symbolizer (SysprofDocumentLoader *self) +{ + g_autoptr(SysprofMultiSymbolizer) multi = NULL; + + g_assert (SYSPROF_IS_DOCUMENT_LOADER (self)); + + g_clear_object (&self->symbolizer); + + multi = sysprof_multi_symbolizer_new (); + sysprof_multi_symbolizer_take (multi, sysprof_bundled_symbolizer_new ()); + self->symbolizer = SYSPROF_SYMBOLIZER (g_steal_pointer (&multi)); +} + static void sysprof_document_loader_finalize (GObject *object) { @@ -140,6 +244,8 @@ static void sysprof_document_loader_init (SysprofDocumentLoader *self) { self->fd = -1; + + set_default_symbolizer (self); } SysprofDocumentLoader * @@ -210,7 +316,11 @@ sysprof_document_loader_set_symbolizer (SysprofDocumentLoader *self, g_return_if_fail (SYSPROF_IS_DOCUMENT_LOADER (self)); if (g_set_object (&self->symbolizer, symbolizer)) - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_SYMBOLIZER]); + { + if (self->symbolizer == NULL) + set_default_symbolizer (self); + g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_SYMBOLIZER]); + } } /** @@ -249,12 +359,11 @@ sysprof_document_loader_get_message (SysprofDocumentLoader *self) } static void -sysprof_document_loader_load_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) +sysprof_document_loader_load_symbols_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) { SysprofDocument *document = (SysprofDocument *)object; - g_autoptr(SysprofDocumentSymbols) symbols = NULL; g_autoptr(GError) error = NULL; g_autoptr(GTask) task = user_data; @@ -262,12 +371,61 @@ sysprof_document_loader_load_cb (GObject *object, g_assert (G_IS_ASYNC_RESULT (result)); g_assert (G_IS_TASK (task)); - if (!(symbols = _sysprof_document_symbolize_finish (document, result, &error))) + if (!_sysprof_document_symbolize_finish (document, result, &error)) g_task_return_error (task, g_steal_pointer (&error)); else g_task_return_pointer (task, g_object_ref (document), g_object_unref); } +static void +sysprof_document_loader_load_document_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + g_autoptr(SysprofDocument) document = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GTask) task = user_data; + SysprofSymbolizer *symbolizer; + + g_assert (G_IS_ASYNC_RESULT (result)); + g_assert (G_IS_TASK (task)); + + symbolizer = g_task_get_task_data (task); + + g_assert (symbolizer != NULL); + g_assert (SYSPROF_IS_SYMBOLIZER (symbolizer)); + + if (!(document = _sysprof_document_new_finish (result, &error))) + g_task_return_error (task, g_steal_pointer (&error)); + else + _sysprof_document_symbolize_async (document, + symbolizer, + g_task_get_cancellable (task), + sysprof_document_loader_load_symbols_cb, + g_object_ref (task)); +} + +static void +sysprof_document_loader_load_mapped_file_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + g_autoptr(GMappedFile) mapped_file = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GTask) task = user_data; + + g_assert (G_IS_ASYNC_RESULT (result)); + g_assert (G_IS_TASK (task)); + + if (!(mapped_file = mapped_file_new_finish (result, &error))) + g_task_return_error (task, g_steal_pointer (&error)); + else + _sysprof_document_new_async (mapped_file, + g_task_get_cancellable (task), + sysprof_document_loader_load_document_cb, + g_object_ref (task)); +} + /** * sysprof_document_loader_load_async: * @self: a #SysprofDocumentLoader @@ -285,43 +443,29 @@ sysprof_document_loader_load_async (SysprofDocumentLoader *self, GAsyncReadyCallback callback, gpointer user_data) { - g_autoptr(SysprofDocument) document = NULL; + g_autoptr(SysprofSymbolizer) symbolizer = NULL; + g_autoptr(GMappedFile) mapped_file = NULL; g_autoptr(GError) error = NULL; g_autoptr(GTask) task = NULL; g_return_if_fail (SYSPROF_IS_DOCUMENT_LOADER (self)); g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); + g_return_if_fail (self->filename != NULL || self->fd != -1); task = g_task_new (self, cancellable, callback, user_data); + g_task_set_task_data (task, g_object_ref (self->symbolizer), g_object_unref); g_task_set_source_tag (task, sysprof_document_loader_load_async); - if (self->fd == -1 && self->filename == NULL) - { - g_task_return_new_error (task, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "SysprofDocumentLoader disposition must have fd or filename set"); - return; - } - if (self->fd != -1) - document = _sysprof_document_new_from_fd (self->fd, &error); - else if (self->filename != NULL) - document = _sysprof_document_new (self->filename, &error); + mapped_file_new_from_fd_async (self->fd, + cancellable, + sysprof_document_loader_load_mapped_file_cb, + g_steal_pointer (&task)); else - g_assert_not_reached (); - - /* TODO: This will probably get renamed to load_async as we want - * to always have a symbolizer for loading. Additionally, the - * document will deal with caches directly instead of the symbols - * object. - */ - - _sysprof_document_symbolize_async (document, - self->symbolizer, - cancellable, - sysprof_document_loader_load_cb, - g_steal_pointer (&task)); + mapped_file_new_async (self->filename, + cancellable, + sysprof_document_loader_load_mapped_file_cb, + g_steal_pointer (&task)); } /** diff --git a/src/libsysprof-analyze/sysprof-document-private.h b/src/libsysprof-analyze/sysprof-document-private.h index 5993e6ea..a289a073 100644 --- a/src/libsysprof-analyze/sysprof-document-private.h +++ b/src/libsysprof-analyze/sysprof-document-private.h @@ -23,24 +23,27 @@ #include #include "sysprof-document.h" +#include "sysprof-symbolizer.h" G_BEGIN_DECLS -SysprofDocument *_sysprof_document_new (const char *filename, - GError **error); -SysprofDocument *_sysprof_document_new_from_fd (int capture_fd, - GError **error); -void _sysprof_document_symbolize_async (SysprofDocument *self, - SysprofSymbolizer *symbolizer, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -SysprofDocumentSymbols *_sysprof_document_symbolize_finish (SysprofDocument *self, - GAsyncResult *result, - GError **error); -gboolean _sysprof_document_is_native (SysprofDocument *self); -char *_sysprof_document_ref_string (SysprofDocument *self, - const char *name); -GtkBitset *_sysprof_document_traceables (SysprofDocument *self); +void _sysprof_document_new_async (GMappedFile *mapped_file, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +SysprofDocument *_sysprof_document_new_finish (GAsyncResult *result, + GError **error); +void _sysprof_document_symbolize_async (SysprofDocument *self, + SysprofSymbolizer *symbolizer, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean _sysprof_document_symbolize_finish (SysprofDocument *self, + GAsyncResult *result, + GError **error); +gboolean _sysprof_document_is_native (SysprofDocument *self); +char *_sysprof_document_ref_string (SysprofDocument *self, + const char *name); +GtkBitset *_sysprof_document_traceables (SysprofDocument *self); G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-document-symbols-private.h b/src/libsysprof-analyze/sysprof-document-symbols-private.h index 58ac1faa..680b6468 100644 --- a/src/libsysprof-analyze/sysprof-document-symbols-private.h +++ b/src/libsysprof-analyze/sysprof-document-symbols-private.h @@ -21,19 +21,28 @@ #pragma once #include "sysprof-document.h" -#include "sysprof-document-symbols.h" #include "sysprof-process-info-private.h" +#include "sysprof-symbolizer.h" +#include "sysprof-symbol.h" G_BEGIN_DECLS -void _sysprof_document_symbols_new (SysprofDocument *document, - SysprofStrings *strings, - SysprofSymbolizer *symbolizer, - GHashTable *pid_to_process_info, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -SysprofDocumentSymbols *_sysprof_document_symbols_new_finish (GAsyncResult *result, - GError **error); +#define SYSPROF_TYPE_DOCUMENT_SYMBOLS (sysprof_document_symbols_get_type()) + +G_DECLARE_FINAL_TYPE (SysprofDocumentSymbols, sysprof_document_symbols, SYSPROF, DOCUMENT_SYMBOLS, GObject) + +void _sysprof_document_symbols_new (SysprofDocument *document, + SysprofStrings *strings, + SysprofSymbolizer *symbolizer, + GHashTable *pid_to_process_info, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +SysprofDocumentSymbols *_sysprof_document_symbols_new_finish (GAsyncResult *result, + GError **error); +SysprofSymbol *_sysprof_document_symbols_lookup (SysprofDocumentSymbols *symbols, + int pid, + SysprofAddressContext context, + SysprofAddress address); G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-document-symbols.c b/src/libsysprof-analyze/sysprof-document-symbols.c index b087d0ab..9c499611 100644 --- a/src/libsysprof-analyze/sysprof-document-symbols.c +++ b/src/libsysprof-analyze/sysprof-document-symbols.c @@ -25,6 +25,7 @@ #include "sysprof-document-symbols-private.h" #include "sysprof-document-traceable.h" #include "sysprof-mount-namespace-private.h" +#include "sysprof-no-symbolizer.h" #include "sysprof-symbol-private.h" #include "sysprof-symbol-cache-private.h" #include "sysprof-symbolizer-private.h" @@ -175,7 +176,8 @@ sysprof_document_symbols_worker (GTask *task, } /* Walk through the available traceables which need symbols extracted */ - if (gtk_bitset_iter_init_first (&iter, bitset, &i)) + if (!SYSPROF_IS_NO_SYMBOLIZER (state->symbolizer) && + gtk_bitset_iter_init_first (&iter, bitset, &i)) { do { @@ -243,7 +245,7 @@ _sysprof_document_symbols_new_finish (GAsyncResult *result, } /** - * sysprof_document_symbols_lookup: + * _sysprof_document_symbols_lookup: * @self: a #SysprofDocumentSymbols * @pid: the process identifier * @context: the #SysprofAddressContext for the address @@ -254,10 +256,10 @@ _sysprof_document_symbols_new_finish (GAsyncResult *result, * Returns: (transfer none) (nullable): a #SysprofSymbol or %NULL */ SysprofSymbol * -sysprof_document_symbols_lookup (SysprofDocumentSymbols *self, - int pid, - SysprofAddressContext context, - SysprofAddress address) +_sysprof_document_symbols_lookup (SysprofDocumentSymbols *self, + int pid, + SysprofAddressContext context, + SysprofAddress address) { SysprofAddressContext new_context; SysprofProcessInfo *process_info; diff --git a/src/libsysprof-analyze/sysprof-document-symbols.h b/src/libsysprof-analyze/sysprof-document-symbols.h deleted file mode 100644 index 556476f5..00000000 --- a/src/libsysprof-analyze/sysprof-document-symbols.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * sysprof-document-symbols.h - * - * Copyright 2023 Christian Hergert - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -#pragma once - -#include - -#include - -#include "sysprof-symbol.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_DOCUMENT_SYMBOLS (sysprof_document_symbols_get_type()) - -SYSPROF_AVAILABLE_IN_ALL -G_DECLARE_FINAL_TYPE (SysprofDocumentSymbols, sysprof_document_symbols, SYSPROF, DOCUMENT_SYMBOLS, GObject) - -SYSPROF_AVAILABLE_IN_ALL -SysprofSymbol *sysprof_document_symbols_lookup (SysprofDocumentSymbols *symbols, - int pid, - SysprofAddressContext context, - SysprofAddress address); - -G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index 0449944a..2e8911e8 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -63,6 +63,8 @@ struct _SysprofDocument SysprofMountNamespace *mount_namespace; + SysprofDocumentSymbols *symbols; + SysprofCaptureFileHeader header; guint needs_swap : 1; }; @@ -197,6 +199,7 @@ sysprof_document_finalize (GObject *object) g_clear_pointer (&self->pids, gtk_bitset_unref); g_clear_object (&self->mount_namespace); + g_clear_object (&self->symbols); g_clear_pointer (&self->files_first_position, g_hash_table_unref); @@ -390,26 +393,34 @@ sysprof_document_load_mountinfos (SysprofDocument *self) } } -static gboolean -sysprof_document_load (SysprofDocument *self, - int capture_fd, - GError **error) +static void +sysprof_document_load_worker (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) { + g_autoptr(SysprofDocument) self = NULL; g_autoptr(GHashTable) files = NULL; + GMappedFile *mapped_file = task_data; goffset pos; gsize len; - g_assert (SYSPROF_IS_DOCUMENT (self)); - g_assert (capture_fd > -1); + g_assert (source_object == NULL); + g_assert (mapped_file != NULL); - if (!(self->mapped_file = g_mapped_file_new_from_fd (capture_fd, FALSE, error))) - return FALSE; - - self->base = (const guint8 *)g_mapped_file_get_contents (self->mapped_file); - len = g_mapped_file_get_length (self->mapped_file); + self = g_object_new (SYSPROF_TYPE_DOCUMENT, NULL); + self->mapped_file = g_mapped_file_ref (mapped_file); + self->base = (const guint8 *)g_mapped_file_get_contents (mapped_file); + len = g_mapped_file_get_length (mapped_file); if (len < sizeof self->header) - return FALSE; + { + g_task_return_new_error (task, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "File header too short"); + return; + } /* Keep a copy of our header */ memcpy (&self->header, self->base, sizeof self->header); @@ -479,77 +490,39 @@ sysprof_document_load (SysprofDocument *self, sysprof_document_load_mountinfos (self); sysprof_document_load_memory_maps (self); - return TRUE; + g_task_return_pointer (task, g_steal_pointer (&self), g_object_unref); } -/** - * _sysprof_document_new_from_fd: - * @capture_fd: a file-descriptor to be mapped - * @error: a location for a #GError, or %NULL - * - * Creates a new memory map using @capture_fd to read the underlying - * Sysprof capture. - * - * No ownership of @capture_fd is transferred, and the caller may close - * @capture_fd after calling this function. - * - * Returns: A #SysprofDocument if successful; otherwise %NULL - * and @error is set. - */ -SysprofDocument * -_sysprof_document_new_from_fd (int capture_fd, - GError **error) +void +_sysprof_document_new_async (GMappedFile *mapped_file, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { - g_autoptr(SysprofDocument) self = NULL; + g_autoptr(GTask) task = NULL; - g_return_val_if_fail (capture_fd > -1, NULL); + g_return_if_fail (mapped_file != NULL); + g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); - self = g_object_new (SYSPROF_TYPE_DOCUMENT, NULL); - - if (!sysprof_document_load (self, capture_fd, error)) - return NULL; - - return g_steal_pointer (&self); + task = g_task_new (NULL, cancellable, callback, user_data); + g_task_set_source_tag (task, _sysprof_document_new_async); + g_task_set_task_data (task, + g_mapped_file_ref (mapped_file), + (GDestroyNotify)g_mapped_file_unref); + g_task_run_in_thread (task, sysprof_document_load_worker); } -/** - * sysprof_document_new: - * @filename: a path to a capture file - * @error: location for a #GError, or %NULL - * - * Similar to sysprof_document_new_from_fd() but opens the file found - * at @filename as a #GMappedFile. - * - * Returns: a #SysprofDocument if successful; otherwise %NULL - * and @error is set. - * - * Since: 45.0 - */ SysprofDocument * -_sysprof_document_new (const char *filename, - GError **error) +_sysprof_document_new_finish (GAsyncResult *result, + GError **error) { - g_autoptr(SysprofDocument) self = NULL; - g_autofd int capture_fd = -1; + SysprofDocument *ret; - g_return_val_if_fail (filename != NULL, NULL); + g_return_val_if_fail (G_IS_TASK (result), NULL); + ret = g_task_propagate_pointer (G_TASK (result), error); + g_return_val_if_fail (!ret || SYSPROF_IS_DOCUMENT (ret), NULL); - if (-1 == (capture_fd = g_open (filename, O_RDONLY|O_CLOEXEC, 0))) - { - int errsv = errno; - g_set_error (error, - G_FILE_ERROR, - g_file_error_from_errno (errsv), - "%s", g_strerror (errsv)); - return NULL; - } - - self = g_object_new (SYSPROF_TYPE_DOCUMENT, NULL); - - if (!sysprof_document_load (self, capture_fd, error)) - return NULL; - - return g_steal_pointer (&self); + return ret; } char * @@ -569,14 +542,25 @@ sysprof_document_symbolize_symbols_cb (GObject *object, g_autoptr(SysprofDocumentSymbols) symbols = NULL; g_autoptr(GTask) task = user_data; g_autoptr(GError) error = NULL; + SysprofDocument *self; g_assert (G_IS_ASYNC_RESULT (result)); g_assert (G_IS_TASK (task)); - if ((symbols = _sysprof_document_symbols_new_finish (result, &error))) - g_task_return_pointer (task, g_steal_pointer (&symbols), g_object_unref); - else - g_task_return_error (task, g_steal_pointer (&error)); + if (!(symbols = _sysprof_document_symbols_new_finish (result, &error))) + { + g_task_return_error (task, g_steal_pointer (&error)); + return; + } + + self = g_task_get_source_object (task); + + g_assert (self != NULL); + g_assert (SYSPROF_IS_DOCUMENT (self)); + + g_set_object (&self->symbols, symbols); + + g_task_return_boolean (task, TRUE); } static void @@ -635,17 +619,17 @@ _sysprof_document_symbolize_async (SysprofDocument *self, g_steal_pointer (&task)); } -SysprofDocumentSymbols * +gboolean _sysprof_document_symbolize_finish (SysprofDocument *self, GAsyncResult *result, GError **error) { - g_return_val_if_fail (SYSPROF_IS_DOCUMENT (self), NULL); - g_return_val_if_fail (G_IS_TASK (result), NULL); - g_return_val_if_fail (g_task_is_valid (result, self), NULL); - g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == _sysprof_document_symbolize_async, NULL); + g_return_val_if_fail (SYSPROF_IS_DOCUMENT (self), FALSE); + g_return_val_if_fail (G_IS_TASK (result), FALSE); + g_return_val_if_fail (g_task_is_valid (result, self), FALSE); + g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == _sysprof_document_symbolize_async, FALSE); - return g_task_propagate_pointer (G_TASK (result), error); + return g_task_propagate_boolean (G_TASK (result), error); } gboolean @@ -796,3 +780,51 @@ sysprof_document_list_processes (SysprofDocument *self) return _sysprof_document_bitset_index_new (G_LIST_MODEL (self), self->processes); } + +/** + * sysprof_document_symbolize_traceable: (skip) + * @self: a #SysprofDocument + * @traceable: the traceable to extract symbols for + * @symbols: an array to store #SysprofSymbols + * @n_symbols: the number of elements in @symbols + * + * Batch symbolizing of a traceable. + * + * No ownership is transfered into @symbols and may be cheaply + * discarded if using the stack for storage. + * + * Returns: The number of symbols or NULL set in @symbols. + */ +guint +sysprof_document_symbolize_traceable (SysprofDocument *self, + SysprofDocumentTraceable *traceable, + SysprofSymbol **symbols, + guint n_symbols) +{ + SysprofAddressContext last_context = SYSPROF_ADDRESS_CONTEXT_NONE; + SysprofAddress *addresses; + guint n_addresses; + int pid; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT (self), 0); + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_TRACEABLE (traceable), 0); + + if (n_symbols == 0 || symbols == NULL) + return 0; + + pid = sysprof_document_frame_get_pid (SYSPROF_DOCUMENT_FRAME (traceable)); + addresses = g_alloca (sizeof (SysprofAddress) * n_symbols); + n_addresses = sysprof_document_traceable_get_stack_addresses (traceable, addresses, n_symbols); + + for (guint i = 0; i < n_addresses; i++) + { + SysprofAddressContext context; + + symbols[i] = _sysprof_document_symbols_lookup (self->symbols, pid, last_context, addresses[i]); + + if (sysprof_address_is_context_switch (addresses[i], &context)) + last_context = context; + } + + return n_addresses; +} diff --git a/src/libsysprof-analyze/sysprof-document.h b/src/libsysprof-analyze/sysprof-document.h index aed68f10..19567d2d 100644 --- a/src/libsysprof-analyze/sysprof-document.h +++ b/src/libsysprof-analyze/sysprof-document.h @@ -25,8 +25,8 @@ #include #include "sysprof-document-file.h" -#include "sysprof-document-symbols.h" -#include "sysprof-symbolizer.h" +#include "sysprof-document-traceable.h" +#include "sysprof-symbol.h" G_BEGIN_DECLS @@ -36,13 +36,18 @@ SYSPROF_AVAILABLE_IN_ALL G_DECLARE_FINAL_TYPE (SysprofDocument, sysprof_document, SYSPROF, DOCUMENT, GObject) SYSPROF_AVAILABLE_IN_ALL -SysprofDocumentFile *sysprof_document_lookup_file (SysprofDocument *self, - const char *path); +SysprofDocumentFile *sysprof_document_lookup_file (SysprofDocument *self, + const char *path); SYSPROF_AVAILABLE_IN_ALL -GListModel *sysprof_document_list_files (SysprofDocument *self); +GListModel *sysprof_document_list_files (SysprofDocument *self); SYSPROF_AVAILABLE_IN_ALL -GListModel *sysprof_document_list_traceables (SysprofDocument *self); +GListModel *sysprof_document_list_traceables (SysprofDocument *self); SYSPROF_AVAILABLE_IN_ALL -GListModel *sysprof_document_list_processes (SysprofDocument *self); +GListModel *sysprof_document_list_processes (SysprofDocument *self); +SYSPROF_AVAILABLE_IN_ALL +guint sysprof_document_symbolize_traceable (SysprofDocument *self, + SysprofDocumentTraceable *traceable, + SysprofSymbol **symbols, + guint n_symbols); G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-multi-symbolizer.c b/src/libsysprof-analyze/sysprof-multi-symbolizer.c index d39008ea..995a3d35 100644 --- a/src/libsysprof-analyze/sysprof-multi-symbolizer.c +++ b/src/libsysprof-analyze/sysprof-multi-symbolizer.c @@ -181,8 +181,8 @@ sysprof_multi_symbolizer_new (void) * that will be queried when @self is queried for symbols. */ void -sysprof_multi_symbolizer_add (SysprofMultiSymbolizer *self, - SysprofSymbolizer *symbolizer) +sysprof_multi_symbolizer_take (SysprofMultiSymbolizer *self, + SysprofSymbolizer *symbolizer) { g_return_if_fail (SYSPROF_IS_MULTI_SYMBOLIZER (self)); g_return_if_fail (SYSPROF_IS_SYMBOLIZER (symbolizer)); diff --git a/src/libsysprof-analyze/sysprof-multi-symbolizer.h b/src/libsysprof-analyze/sysprof-multi-symbolizer.h index 9beffe91..2ac53f47 100644 --- a/src/libsysprof-analyze/sysprof-multi-symbolizer.h +++ b/src/libsysprof-analyze/sysprof-multi-symbolizer.h @@ -37,7 +37,7 @@ GType sysprof_multi_symbolizer_get_type (void) G_GNUC_CONST; SYSPROF_AVAILABLE_IN_ALL SysprofMultiSymbolizer *sysprof_multi_symbolizer_new (void); SYSPROF_AVAILABLE_IN_ALL -void sysprof_multi_symbolizer_add (SysprofMultiSymbolizer *self, +void sysprof_multi_symbolizer_take (SysprofMultiSymbolizer *self, SysprofSymbolizer *symbolizer); G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofMultiSymbolizer, g_object_unref) diff --git a/src/libsysprof-analyze/sysprof-no-symbolizer.c b/src/libsysprof-analyze/sysprof-no-symbolizer.c index 9f803c5c..372fcfe7 100644 --- a/src/libsysprof-analyze/sysprof-no-symbolizer.c +++ b/src/libsysprof-analyze/sysprof-no-symbolizer.c @@ -21,12 +21,18 @@ #include "config.h" #include "sysprof-no-symbolizer.h" +#include "sysprof-symbolizer-private.h" struct _SysprofNoSymbolizer { SysprofSymbolizer parent_instance; }; +struct _SysprofNoSymbolizerClass +{ + SysprofSymbolizerClass parent_class; +}; + G_DEFINE_FINAL_TYPE (SysprofNoSymbolizer, sysprof_no_symbolizer, SYSPROF_TYPE_SYMBOLIZER) static SysprofSymbol * @@ -51,8 +57,20 @@ sysprof_no_symbolizer_init (SysprofNoSymbolizer *self) { } +/** + * sysprof_no_symbolizer_get: + * + * Gets a #SysprofSymbolizer that will never symbolize. + * + * Returns: (transfer none): a #SysprofSymbolizer + */ SysprofSymbolizer * -sysprof_no_symbolizer_new (void) +sysprof_no_symbolizer_get (void) { - return g_object_new (SYSPROF_TYPE_NO_SYMBOLIZER, NULL); + static SysprofSymbolizer *instance; + + if (g_once_init_enter (&instance)) + g_once_init_leave (&instance, g_object_new (SYSPROF_TYPE_NO_SYMBOLIZER, NULL)); + + return instance; } diff --git a/src/libsysprof-analyze/sysprof-no-symbolizer.h b/src/libsysprof-analyze/sysprof-no-symbolizer.h index ebe620b1..e958b670 100644 --- a/src/libsysprof-analyze/sysprof-no-symbolizer.h +++ b/src/libsysprof-analyze/sysprof-no-symbolizer.h @@ -20,18 +20,23 @@ #pragma once -#include - #include "sysprof-symbolizer.h" G_BEGIN_DECLS -#define SYSPROF_TYPE_NO_SYMBOLIZER (sysprof_no_symbolizer_get_type()) +#define SYSPROF_TYPE_NO_SYMBOLIZER (sysprof_no_symbolizer_get_type()) +#define SYSPROF_IS_NO_SYMBOLIZER(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_NO_SYMBOLIZER) +#define SYSPROF_NO_SYMBOLIZER(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_NO_SYMBOLIZER, SysprofNoSymbolizer) +#define SYSPROF_NO_SYMBOLIZER_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_NO_SYMBOLIZER, SysprofNoSymbolizerClass) + +typedef struct _SysprofNoSymbolizer SysprofNoSymbolizer; +typedef struct _SysprofNoSymbolizerClass SysprofNoSymbolizerClass; SYSPROF_AVAILABLE_IN_ALL -G_DECLARE_DERIVABLE_TYPE (SysprofNoSymbolizer, sysprof_no_symbolizer, SYSPROF, NO_SYMBOLIZER, SysprofSymbolizer) - +GType sysprof_no_symbolizer_get_type (void) G_GNUC_CONST; SYSPROF_AVAILABLE_IN_ALL -SysprofSymbolizer *sysprof_no_symbolizer_new (void); +SysprofSymbolizer *sysprof_no_symbolizer_get (void); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofNoSymbolizer, g_object_unref) G_END_DECLS diff --git a/src/libsysprof-analyze/tests/test-capture-model.c b/src/libsysprof-analyze/tests/test-capture-model.c index 2e16ee44..ee4252b4 100644 --- a/src/libsysprof-analyze/tests/test-capture-model.c +++ b/src/libsysprof-analyze/tests/test-capture-model.c @@ -9,9 +9,10 @@ int main (int argc, char *argv[]) { - SysprofDocument *document; + g_autoptr(SysprofDocumentLoader) loader = NULL; + g_autoptr(SysprofDocument) document = NULL; + g_autoptr(GError) error = NULL; const char *filename; - GError *error = NULL; guint n_items; sysprof_clock_init (); @@ -24,7 +25,10 @@ main (int argc, filename = argv[1]; - if (!(document = _sysprof_document_new (filename, &error))) + loader = sysprof_document_loader_new (filename); + sysprof_document_loader_set_symbolizer (loader, sysprof_no_symbolizer_get ()); + + if (!(document = sysprof_document_loader_load (loader, NULL, &error))) { g_printerr ("Failed to load %s: %s\n", filename, error->message); @@ -88,7 +92,5 @@ main (int argc, g_printerr ("%u frames\n", n_items); - g_clear_object (&document); - return 0; } diff --git a/src/libsysprof-analyze/tests/test-list-files.c b/src/libsysprof-analyze/tests/test-list-files.c index a79c4a2d..78b3ae06 100644 --- a/src/libsysprof-analyze/tests/test-list-files.c +++ b/src/libsysprof-analyze/tests/test-list-files.c @@ -26,6 +26,7 @@ int main (int argc, char *argv[]) { + g_autoptr(SysprofDocumentLoader) loader = NULL; g_autoptr(SysprofDocument) document = NULL; g_autoptr(GListModel) files = NULL; g_autoptr(GError) error = NULL; @@ -37,7 +38,10 @@ main (int argc, return 1; } - if (!(document = _sysprof_document_new (argv[1], &error))) + loader = sysprof_document_loader_new (argv[1]); + sysprof_document_loader_set_symbolizer (loader, sysprof_no_symbolizer_get ()); + + if (!(document = sysprof_document_loader_load (loader, NULL, &error))) { g_printerr ("Failed to open capture: %s\n", error->message); return 1; diff --git a/src/libsysprof-analyze/tests/test-list-processes.c b/src/libsysprof-analyze/tests/test-list-processes.c index d3d419da..5b4fffcf 100644 --- a/src/libsysprof-analyze/tests/test-list-processes.c +++ b/src/libsysprof-analyze/tests/test-list-processes.c @@ -26,6 +26,7 @@ int main (int argc, char *argv[]) { + g_autoptr(SysprofDocumentLoader) loader = NULL; g_autoptr(SysprofDocument) document = NULL; g_autoptr(GListModel) processes = NULL; g_autoptr(GError) error = NULL; @@ -37,7 +38,10 @@ main (int argc, return 1; } - if (!(document = _sysprof_document_new (argv[1], &error))) + loader = sysprof_document_loader_new (argv[1]); + sysprof_document_loader_set_symbolizer (loader, sysprof_no_symbolizer_get ()); + + if (!(document = sysprof_document_loader_load (loader, NULL, &error))) { g_printerr ("Failed to open capture: %s\n", error->message); return 1; diff --git a/src/libsysprof-analyze/tests/test-print-file.c b/src/libsysprof-analyze/tests/test-print-file.c index cd8d3720..1e49f19d 100644 --- a/src/libsysprof-analyze/tests/test-print-file.c +++ b/src/libsysprof-analyze/tests/test-print-file.c @@ -26,6 +26,7 @@ int main (int argc, char *argv[]) { + g_autoptr(SysprofDocumentLoader) loader = NULL; g_autoptr(SysprofDocumentFile) file = NULL; g_autoptr(SysprofDocument) document = NULL; g_autoptr(GListModel) files = NULL; @@ -38,7 +39,10 @@ main (int argc, return 1; } - if (!(document = _sysprof_document_new (argv[1], &error))) + loader = sysprof_document_loader_new (argv[1]); + sysprof_document_loader_set_symbolizer (loader, sysprof_no_symbolizer_get ()); + + if (!(document = sysprof_document_loader_load (loader, NULL, &error))) { g_printerr ("Failed to open capture: %s\n", error->message); return 1; diff --git a/src/libsysprof-analyze/tests/test-symbolize.c b/src/libsysprof-analyze/tests/test-symbolize.c index 8265d2da..29e5e3ef 100644 --- a/src/libsysprof-analyze/tests/test-symbolize.c +++ b/src/libsysprof-analyze/tests/test-symbolize.c @@ -7,63 +7,88 @@ static GMainLoop *main_loop; static void -symbolize_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) +load_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) { - SysprofDocument *document = (SysprofDocument *)object; - g_autoptr(SysprofDocumentSymbols) symbols = NULL; + SysprofDocumentLoader *loader = (SysprofDocumentLoader *)object; + g_autoptr(SysprofDocument) document = NULL; g_autoptr(GListModel) traceables = NULL; g_autoptr(GError) error = NULL; - SysprofAddress addresses[128]; + g_autoptr(GString) str = NULL; + SysprofSymbol *symbols[128]; + guint n_symbols; guint n_items; - g_assert (SYSPROF_IS_DOCUMENT (document)); + g_assert (SYSPROF_IS_DOCUMENT_LOADER (loader)); g_assert (G_IS_ASYNC_RESULT (result)); - if (!(symbols = _sysprof_document_symbolize_finish (document, result, &error))) - g_error ("Failed to symbolize: %s", error->message); + if (!(document = sysprof_document_loader_load_finish (loader, result, &error))) + g_error ("Failed to load document: %s", error->message); traceables = sysprof_document_list_traceables (document); n_items = g_list_model_get_n_items (traceables); + str = g_string_new (""); + for (guint i = 0; i < n_items; i++) { g_autoptr(SysprofDocumentTraceable) traceable = g_list_model_get_item (traceables, i); - SysprofAddressContext last_context; - guint depth; - int pid; + + str->len = 0; + str->str[0] = 0; g_assert (traceable != NULL); g_assert (SYSPROF_IS_DOCUMENT_TRACEABLE (traceable)); - last_context = SYSPROF_ADDRESS_CONTEXT_NONE; - pid = sysprof_document_frame_get_pid (SYSPROF_DOCUMENT_FRAME (traceable)); - depth = sysprof_document_traceable_get_stack_addresses (traceable, addresses, G_N_ELEMENTS (addresses)); + n_symbols = sysprof_document_symbolize_traceable (document, + traceable, + symbols, + G_N_ELEMENTS (symbols)); g_print ("%s depth=%u pid=%u\n", - G_OBJECT_TYPE_NAME (traceable), depth, pid); + G_OBJECT_TYPE_NAME (traceable), + n_symbols, + sysprof_document_frame_get_pid (SYSPROF_DOCUMENT_FRAME (traceable))); - for (guint j = 0; j < depth; j++) + for (guint j = 0; j < n_symbols; j++) { - SysprofAddress address = addresses[j]; - SysprofSymbol *symbol = sysprof_document_symbols_lookup (symbols, pid, last_context, address); - SysprofAddressContext context; - - if (sysprof_address_is_context_switch (address, &context)) - last_context = context; + SysprofSymbol *symbol = symbols[j]; + const char *name; + const char *path; + const char *nick; if (symbol != NULL) - g_print (" %02d: %p: %s [%s]\n", - j, - GSIZE_TO_POINTER (address), - sysprof_symbol_get_name (symbol), - sysprof_symbol_get_binary_path (symbol) ?: ""); + { + name = sysprof_symbol_get_name (symbol); + path = sysprof_symbol_get_binary_path (symbol); + nick = sysprof_symbol_get_binary_nick (symbol); + } else - g_print (" %02d: %p:\n", j, GSIZE_TO_POINTER (address)); + { + name = path = nick = NULL; + } + + g_string_append_printf (str, + " %02d: 0x%"G_GINT64_MODIFIER"x:", + j, + sysprof_document_traceable_get_stack_address (traceable, j)); + + if (name) + g_string_append_printf (str, " %s", name); + + if (path) + g_string_append_printf (str, " [%s]", path); + + if (nick) + g_string_append_printf (str, " (%s)", nick); + + g_string_append_c (str, '\n'); } - g_print (" ================\n"); + g_string_append (str, " ================\n"); + + write (STDOUT_FILENO, str->str, str->len); } g_print ("Document symbolized\n"); @@ -75,10 +100,8 @@ int main (int argc, char *argv[]) { - g_autoptr(SysprofDocument) document = NULL; - g_autoptr(SysprofMultiSymbolizer) multi = NULL; + g_autoptr(SysprofDocumentLoader) loader = NULL; g_autoptr(GError) error = NULL; - const char *filename; main_loop = g_main_loop_new (NULL, FALSE); @@ -88,23 +111,8 @@ main (int argc, return 1; } - filename = argv[1]; - - if (!(document = _sysprof_document_new (filename, &error))) - { - g_printerr ("Failed to load document: %s\n", error->message); - return 1; - } - - multi = sysprof_multi_symbolizer_new (); - sysprof_multi_symbolizer_add (multi, sysprof_bundled_symbolizer_new ()); - - _sysprof_document_symbolize_async (document, - SYSPROF_SYMBOLIZER (multi), - NULL, - symbolize_cb, - NULL); - + loader = sysprof_document_loader_new (argv[1]); + sysprof_document_loader_load_async (loader, NULL, load_cb, NULL); g_main_loop_run (main_loop); return 0; From 580889f8cb68b146a076df0b230ebdbc091ed3f7 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 15 May 2023 11:20:44 -0700 Subject: [PATCH 0129/1030] libsysprof-analyze: short-circuit when address > max This adds an O(1) check at the head of the lookup to avoid looking at every RB_RIGHT() in the tree when address falls beyond the upper bound of the interval tree. --- src/libsysprof-analyze/sysprof-symbol-cache.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-symbol-cache.c b/src/libsysprof-analyze/sysprof-symbol-cache.c index 3a8c5792..b657a380 100644 --- a/src/libsysprof-analyze/sysprof-symbol-cache.c +++ b/src/libsysprof-analyze/sysprof-symbol-cache.c @@ -178,6 +178,13 @@ sysprof_symbol_cache_lookup (SysprofSymbolCache *self, node = RB_ROOT(&self->head); + /* The root node contains our calculated max as augmented in RBTree. + * Therefore, we can know if @address falls beyond the upper bound + * in O(1) without having to add a branch to the while loop below. + */ + if (node == NULL || node->max < address) + return NULL; + while (node != NULL) { g_assert (RB_LEFT(node, link) == NULL || From 11f053159187899c4793376946dc2256860268e3 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 15 May 2023 12:15:08 -0700 Subject: [PATCH 0130/1030] libsysprof: include gzip'd /proc/kallsyms in capture Compressed, this adds about 2.5mb to the capture file for the contents of the kallsyms. However, that is useful so that we can decode kernel symbols after the fact without relying on __symbols__ to be tacked on by the recording machine. --- src/libsysprof/sysprof-proc-source.c | 96 +++++++++++++++++++++++++++- 1 file changed, 95 insertions(+), 1 deletion(-) diff --git a/src/libsysprof/sysprof-proc-source.c b/src/libsysprof/sysprof-proc-source.c index b59d1d58..ba969e24 100644 --- a/src/libsysprof/sysprof-proc-source.c +++ b/src/libsysprof/sysprof-proc-source.c @@ -39,11 +39,12 @@ #include "config.h" -#include #include #include #include +#include + #include "sysprof-helpers.h" #include "sysprof-podman.h" #include "sysprof-proc-source.h" @@ -375,6 +376,86 @@ sysprof_proc_source_get_process_info_cb (GObject *object, sysprof_source_emit_finished (SYSPROF_SOURCE (self)); } +typedef struct _StreamToCapture +{ + SysprofCaptureWriter *writer; + GInputStream *stream; + guint8 *buf; + gsize buflen; +} StreamToCapture; + +static void +stream_to_capture_free (StreamToCapture *state) +{ + g_clear_pointer (&state->writer, sysprof_capture_writer_unref); + g_clear_pointer (&state->buf, g_free); + g_clear_object (&state->stream); + g_free (state); +} + +static gboolean +stream_to_capture (StreamToCapture *state) +{ + gssize n_read; + + g_assert (state != NULL); + g_assert (state->writer != NULL); + g_assert (state->buf != NULL); + g_assert (state->buflen > 0); + g_assert (G_IS_INPUT_STREAM (state->stream)); + + n_read = g_input_stream_read (state->stream, state->buf, state->buflen, NULL, NULL); + + if (n_read >= 0) + sysprof_capture_writer_add_file (state->writer, + SYSPROF_CAPTURE_CURRENT_TIME, + -1, + -1, + "/proc/kallsyms.gz", + n_read == 0, + state->buf, + n_read); + + return n_read > 0; +} + +static void +get_kallsyms_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + SysprofHelpers *helpers = (SysprofHelpers *)object; + g_autoptr(SysprofProcSource) self = user_data; + g_autoptr(GZlibCompressor) compressor = NULL; + g_autoptr(GInputStream) base_stream = NULL; + g_autoptr(GInputStream) gz_stream = NULL; + g_autoptr(GError) error = NULL; + g_autofree char *contents = NULL; + StreamToCapture *state; + + g_assert (SYSPROF_IS_HELPERS (helpers)); + g_assert (G_IS_ASYNC_RESULT (result)); + g_assert (SYSPROF_IS_PROC_SOURCE (self)); + + if (!sysprof_helpers_get_proc_file_finish (helpers, result, &contents, &error)) + return; + + base_stream = g_memory_input_stream_new_from_data (g_steal_pointer (&contents), -1, g_free); + compressor = g_zlib_compressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP, 6); + gz_stream = g_converter_input_stream_new (base_stream, G_CONVERTER (compressor)); + + state = g_new0 (StreamToCapture, 1); + state->stream = g_object_ref (gz_stream); + state->writer = sysprof_capture_writer_ref (self->writer); + state->buflen = 16100; /* 16kb write buffer - framing overhead */ + state->buf = g_malloc (state->buflen); + + g_idle_add_full (G_PRIORITY_HIGH, + (GSourceFunc)stream_to_capture, + state, + (GDestroyNotify)stream_to_capture_free); +} + static void sysprof_proc_source_start (SysprofSource *source) { @@ -389,6 +470,19 @@ sysprof_proc_source_start (SysprofSource *source) NULL, sysprof_proc_source_get_process_info_cb, g_object_ref (self)); + + /* We must read kallsyms from sysprofd because if we ask for a FD and read + * it back from our current process, it's likely the kernel will censor our + * addresses making them useless. + * + * That means a pretty large (couple MB transfer) over D-Bus which is not + * great but also not the end of the world so long as we do it async. + */ + sysprof_helpers_get_proc_file_async (helpers, + "/proc/kallsyms", + NULL, + get_kallsyms_cb, + g_object_ref (self)); } static void From fa55594e238ffc109b6fa5ef2650ffe287ae7b95 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 15 May 2023 12:46:46 -0700 Subject: [PATCH 0131/1030] libsysprof-analyze: make return type GRefString --- src/libsysprof-analyze/sysprof-document-private.h | 2 +- src/libsysprof-analyze/sysprof-document.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-document-private.h b/src/libsysprof-analyze/sysprof-document-private.h index a289a073..75f4a783 100644 --- a/src/libsysprof-analyze/sysprof-document-private.h +++ b/src/libsysprof-analyze/sysprof-document-private.h @@ -42,7 +42,7 @@ gboolean _sysprof_document_symbolize_finish (SysprofDocument *self, GAsyncResult *result, GError **error); gboolean _sysprof_document_is_native (SysprofDocument *self); -char *_sysprof_document_ref_string (SysprofDocument *self, +GRefString *_sysprof_document_ref_string (SysprofDocument *self, const char *name); GtkBitset *_sysprof_document_traceables (SysprofDocument *self); diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index 2e8911e8..83384564 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -525,7 +525,7 @@ _sysprof_document_new_finish (GAsyncResult *result, return ret; } -char * +GRefString * _sysprof_document_ref_string (SysprofDocument *self, const char *name) { From c6135ac53825c3383717ed9cc1043c7c9865bcc8 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 15 May 2023 13:03:46 -0700 Subject: [PATCH 0132/1030] libsysprof-analyze: parse embedded /proc/kallsyms.gz This is useful so that even if we do not get __symbols__ in the capture file we can decode symbols from the target machine. --- .../sysprof-kallsyms-symbolizer.c | 88 ++++++++++++++++++- 1 file changed, 86 insertions(+), 2 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-kallsyms-symbolizer.c b/src/libsysprof-analyze/sysprof-kallsyms-symbolizer.c index e2d6dd4f..6b2d62a7 100644 --- a/src/libsysprof-analyze/sysprof-kallsyms-symbolizer.c +++ b/src/libsysprof-analyze/sysprof-kallsyms-symbolizer.c @@ -20,14 +20,24 @@ #include "config.h" +#include + #include "sysprof-kallsyms-symbolizer.h" #include "sysprof-document-private.h" +#include "sysprof-strings-private.h" #include "sysprof-symbolizer-private.h" #include "sysprof-symbol-private.h" +typedef struct _KernelSymbol +{ + guint64 address; + GRefString *name; +} KernelSymbol; + struct _SysprofKallsymsSymbolizer { - SysprofSymbolizer parent_instance; + SysprofSymbolizer parent_instance; + GArray *kallsyms; }; struct _SysprofKallsymsSymbolizerClass @@ -37,6 +47,26 @@ struct _SysprofKallsymsSymbolizerClass G_DEFINE_FINAL_TYPE (SysprofKallsymsSymbolizer, sysprof_kallsyms_symbolizer, SYSPROF_TYPE_SYMBOLIZER) +static SysprofStrings *kallsym_strings; + +static void +kernel_symbol_clear (gpointer data) +{ + KernelSymbol *symbol = data; + + g_clear_pointer (&symbol->name, g_ref_string_release); +} + +static inline void +sysprof_kallsyms_symbolizer_add (SysprofKallsymsSymbolizer *self, + guint64 address, + guint8 type, + const char *name) +{ + const KernelSymbol s = { address, sysprof_strings_get (kallsym_strings, name) }; + g_array_append_val (self->kallsyms, s); +} + static void sysprof_kallsyms_symbolizer_prepare_worker (GTask *task, gpointer source_object, @@ -55,11 +85,57 @@ sysprof_kallsyms_symbolizer_prepare_worker (GTask *task, while ((line = g_data_input_stream_read_line_utf8 (input, &len, cancellable, &error))) { - /* TODO: port kallsym parser from sysprof-kallsyms.c */ + const char *endptr = &line[len]; + const char *name; + guint64 address; + char *iter = line; + guint8 type; + address = g_ascii_strtoull (iter, &iter, 16); + + if G_UNLIKELY ((address == 0 && errno == EINVAL) || + (address == G_MAXUINT64 && errno == ERANGE)) + goto failure; + + if G_UNLIKELY (iter[0] != ' ') + goto failure; + + /* Swallow space */ + iter++; + if (iter >= endptr) + goto failure; + + /* Get type 'ABDRTVWabdrtw' */ + type = iter[0]; + + /* Move past type and space */ + iter++; + iter++; + if (iter >= endptr) + goto failure; + + /* Name starts here */ + name = iter; + + /* Walk ahead to first space or \0 */ + while (iter < endptr && !g_ascii_isspace (*iter)) + iter++; + if (iter > endptr) + goto failure; + + /* Make @name usable as C string */ + *iter = 0; + + sysprof_kallsyms_symbolizer_add (self, address, type, name); + + failure: g_free (line); } + /* Symbols are already sorted in kallsyms, so no need to g_array_sort(). + * We just trust that the kernel did that part correctly. + */ + g_task_return_boolean (task, TRUE); } @@ -127,6 +203,10 @@ sysprof_kallsyms_symbolizer_symbolize (SysprofSymbolizer *symbolizer, static void sysprof_kallsyms_symbolizer_finalize (GObject *object) { + SysprofKallsymsSymbolizer *self = (SysprofKallsymsSymbolizer *)object; + + g_clear_pointer (&self->kallsyms, g_array_unref); + G_OBJECT_CLASS (sysprof_kallsyms_symbolizer_parent_class)->finalize (object); } @@ -141,11 +221,15 @@ sysprof_kallsyms_symbolizer_class_init (SysprofKallsymsSymbolizerClass *klass) symbolizer_class->prepare_async = sysprof_kallsyms_symbolizer_prepare_async; symbolizer_class->prepare_finish = sysprof_kallsyms_symbolizer_prepare_finish; symbolizer_class->symbolize = sysprof_kallsyms_symbolizer_symbolize; + + kallsym_strings = sysprof_strings_new (); } static void sysprof_kallsyms_symbolizer_init (SysprofKallsymsSymbolizer *self) { + self->kallsyms = g_array_new (FALSE, FALSE, sizeof (KernelSymbol)); + g_array_set_clear_func (self->kallsyms, kernel_symbol_clear); } SysprofSymbolizer * From 14e5cf06a5134c23da5a70bac20f9e1df5e67d53 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 15 May 2023 13:04:28 -0700 Subject: [PATCH 0133/1030] libsysprof-analyzer: include kallsym symbolizer in default Generally we're capturing Linux systems, and even if not, the capture may contain embedded Linux symbols if on a secondary system. --- src/libsysprof-analyze/sysprof-document-loader.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-document-loader.c b/src/libsysprof-analyze/sysprof-document-loader.c index cfe65c52..0c6e5d00 100644 --- a/src/libsysprof-analyze/sysprof-document-loader.c +++ b/src/libsysprof-analyze/sysprof-document-loader.c @@ -26,6 +26,7 @@ #include "sysprof-bundled-symbolizer.h" #include "sysprof-document-loader.h" #include "sysprof-document-private.h" +#include "sysprof-kallsyms-symbolizer.h" #include "sysprof-multi-symbolizer.h" struct _SysprofDocumentLoader @@ -146,6 +147,7 @@ set_default_symbolizer (SysprofDocumentLoader *self) multi = sysprof_multi_symbolizer_new (); sysprof_multi_symbolizer_take (multi, sysprof_bundled_symbolizer_new ()); + sysprof_multi_symbolizer_take (multi, sysprof_kallsyms_symbolizer_new ()); self->symbolizer = SYSPROF_SYMBOLIZER (g_steal_pointer (&multi)); } From 525b30a42f169aa96d6f0459131d1e4c847fa120 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 15 May 2023 13:07:48 -0700 Subject: [PATCH 0134/1030] libsysprof-analyze: give symbolizer access to address context The kallsym symbol resolver will need this to short-circuit unless we're within a kernel address context. --- src/libsysprof-analyze/sysprof-bundled-symbolizer.c | 1 + src/libsysprof-analyze/sysprof-document-symbols.c | 2 +- src/libsysprof-analyze/sysprof-kallsyms-symbolizer.c | 1 + src/libsysprof-analyze/sysprof-multi-symbolizer.c | 3 ++- src/libsysprof-analyze/sysprof-no-symbolizer.c | 1 + src/libsysprof-analyze/sysprof-symbolizer-private.h | 2 ++ src/libsysprof-analyze/sysprof-symbolizer.c | 3 ++- 7 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-bundled-symbolizer.c b/src/libsysprof-analyze/sysprof-bundled-symbolizer.c index 875a67a5..be28d8e7 100644 --- a/src/libsysprof-analyze/sysprof-bundled-symbolizer.c +++ b/src/libsysprof-analyze/sysprof-bundled-symbolizer.c @@ -172,6 +172,7 @@ static SysprofSymbol * sysprof_bundled_symbolizer_symbolize (SysprofSymbolizer *symbolizer, SysprofStrings *strings, const SysprofProcessInfo *process_info, + SysprofAddressContext context, SysprofAddress address) { SysprofBundledSymbolizer *self = SYSPROF_BUNDLED_SYMBOLIZER (symbolizer); diff --git a/src/libsysprof-analyze/sysprof-document-symbols.c b/src/libsysprof-analyze/sysprof-document-symbols.c index 9c499611..13a76d03 100644 --- a/src/libsysprof-analyze/sysprof-document-symbols.c +++ b/src/libsysprof-analyze/sysprof-document-symbols.c @@ -120,7 +120,7 @@ add_traceable (SysprofStrings *strings, } else { - g_autoptr(SysprofSymbol) symbol = _sysprof_symbolizer_symbolize (symbolizer, strings, process_info, address); + g_autoptr(SysprofSymbol) symbol = _sysprof_symbolizer_symbolize (symbolizer, strings, process_info, last_context, address); if (symbol != NULL) sysprof_symbol_cache_take (process_info->symbol_cache, g_steal_pointer (&symbol)); diff --git a/src/libsysprof-analyze/sysprof-kallsyms-symbolizer.c b/src/libsysprof-analyze/sysprof-kallsyms-symbolizer.c index 6b2d62a7..98a54d1c 100644 --- a/src/libsysprof-analyze/sysprof-kallsyms-symbolizer.c +++ b/src/libsysprof-analyze/sysprof-kallsyms-symbolizer.c @@ -195,6 +195,7 @@ static SysprofSymbol * sysprof_kallsyms_symbolizer_symbolize (SysprofSymbolizer *symbolizer, SysprofStrings *strings, const SysprofProcessInfo *process_info, + SysprofAddressContext context, SysprofAddress address) { return NULL; diff --git a/src/libsysprof-analyze/sysprof-multi-symbolizer.c b/src/libsysprof-analyze/sysprof-multi-symbolizer.c index 995a3d35..483cdde5 100644 --- a/src/libsysprof-analyze/sysprof-multi-symbolizer.c +++ b/src/libsysprof-analyze/sysprof-multi-symbolizer.c @@ -121,6 +121,7 @@ static SysprofSymbol * sysprof_multi_symbolizer_symbolize (SysprofSymbolizer *symbolizer, SysprofStrings *strings, const SysprofProcessInfo *process_info, + SysprofAddressContext context, SysprofAddress address) { SysprofMultiSymbolizer *self = SYSPROF_MULTI_SYMBOLIZER (symbolizer); @@ -128,7 +129,7 @@ sysprof_multi_symbolizer_symbolize (SysprofSymbolizer *symbolizer, for (guint i = 0; i < self->symbolizers->len; i++) { SysprofSymbolizer *child = g_ptr_array_index (self->symbolizers, i); - SysprofSymbol *symbol = _sysprof_symbolizer_symbolize (child, strings, process_info, address); + SysprofSymbol *symbol = _sysprof_symbolizer_symbolize (child, strings, process_info, context, address); if (symbol != NULL) return symbol; diff --git a/src/libsysprof-analyze/sysprof-no-symbolizer.c b/src/libsysprof-analyze/sysprof-no-symbolizer.c index 372fcfe7..0b0e5070 100644 --- a/src/libsysprof-analyze/sysprof-no-symbolizer.c +++ b/src/libsysprof-analyze/sysprof-no-symbolizer.c @@ -39,6 +39,7 @@ static SysprofSymbol * sysprof_no_symbolizer_symbolize (SysprofSymbolizer *symbolizer, SysprofStrings *strings, const SysprofProcessInfo *process_info, + SysprofAddressContext context, SysprofAddress address) { return NULL; diff --git a/src/libsysprof-analyze/sysprof-symbolizer-private.h b/src/libsysprof-analyze/sysprof-symbolizer-private.h index fadc1f7f..dd917e44 100644 --- a/src/libsysprof-analyze/sysprof-symbolizer-private.h +++ b/src/libsysprof-analyze/sysprof-symbolizer-private.h @@ -52,6 +52,7 @@ struct _SysprofSymbolizerClass SysprofSymbol *(*symbolize) (SysprofSymbolizer *self, SysprofStrings *strings, const SysprofProcessInfo *process_info, + SysprofAddressContext context, SysprofAddress address); }; @@ -67,6 +68,7 @@ gboolean _sysprof_symbolizer_prepare_finish (SysprofSymbolizer *se SysprofSymbol *_sysprof_symbolizer_symbolize (SysprofSymbolizer *self, SysprofStrings *strings, const SysprofProcessInfo *process_info, + SysprofAddressContext context, SysprofAddress address); G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-symbolizer.c b/src/libsysprof-analyze/sysprof-symbolizer.c index 14eca803..9ad17ca2 100644 --- a/src/libsysprof-analyze/sysprof-symbolizer.c +++ b/src/libsysprof-analyze/sysprof-symbolizer.c @@ -94,7 +94,8 @@ SysprofSymbol * _sysprof_symbolizer_symbolize (SysprofSymbolizer *self, SysprofStrings *strings, const SysprofProcessInfo *process_info, + SysprofAddressContext context, SysprofAddress address) { - return SYSPROF_SYMBOLIZER_GET_CLASS (self)->symbolize (self, strings, process_info, address); + return SYSPROF_SYMBOLIZER_GET_CLASS (self)->symbolize (self, strings, process_info, context, address); } From 131d9fba298e1bc7e3cbdab6097e68b87a0771ee Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 15 May 2023 13:30:41 -0700 Subject: [PATCH 0135/1030] libsysprof-analyze: implement kallsyms symbolizer This does a simple binary search across the parsed kallsyms using the addresses we've parsed. We need to be sure we've created the array properly so that our bounds checking will prevent infinite loops in the tight binary search loop. --- .../sysprof-kallsyms-symbolizer.c | 72 ++++++++++++++++++- 1 file changed, 69 insertions(+), 3 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-kallsyms-symbolizer.c b/src/libsysprof-analyze/sysprof-kallsyms-symbolizer.c index 98a54d1c..7c4d0a78 100644 --- a/src/libsysprof-analyze/sysprof-kallsyms-symbolizer.c +++ b/src/libsysprof-analyze/sysprof-kallsyms-symbolizer.c @@ -38,6 +38,8 @@ struct _SysprofKallsymsSymbolizer { SysprofSymbolizer parent_instance; GArray *kallsyms; + guint64 low; + guint64 high; }; struct _SysprofKallsymsSymbolizerClass @@ -48,6 +50,7 @@ struct _SysprofKallsymsSymbolizerClass G_DEFINE_FINAL_TYPE (SysprofKallsymsSymbolizer, sysprof_kallsyms_symbolizer, SYSPROF_TYPE_SYMBOLIZER) static SysprofStrings *kallsym_strings; +static GRefString *linux_string; static void kernel_symbol_clear (gpointer data) @@ -76,6 +79,7 @@ sysprof_kallsyms_symbolizer_prepare_worker (GTask *task, SysprofKallsymsSymbolizer *self = source_object; GDataInputStream *input = task_data; g_autoptr(GError) error = NULL; + guint64 last_address = 0; char *line; gsize len; @@ -126,7 +130,14 @@ sysprof_kallsyms_symbolizer_prepare_worker (GTask *task, /* Make @name usable as C string */ *iter = 0; - sysprof_kallsyms_symbolizer_add (self, address, type, name); + /* Only add this if we're after the previous address so that + * we can be sure our sort is proper and will not break the + * tight loop in lookup binary search. + */ + if (address > last_address) + sysprof_kallsyms_symbolizer_add (self, address, type, name); + + last_address = address; failure: g_free (line); @@ -136,6 +147,19 @@ sysprof_kallsyms_symbolizer_prepare_worker (GTask *task, * We just trust that the kernel did that part correctly. */ + /* Store a "best guess" at an lower/upper bound for the max address so that + * we can avoid searching for anything unreasonably past the end of the last + * kernel symbol. + */ + if (self->kallsyms->len > 0) + { + const KernelSymbol *head = &g_array_index (self->kallsyms, KernelSymbol, 0); + const KernelSymbol *tail = &g_array_index (self->kallsyms, KernelSymbol, self->kallsyms->len-1); + + self->low = head->address; + self->high = tail->address + 0xFFFF; + } + g_task_return_boolean (task, TRUE); } @@ -198,7 +222,48 @@ sysprof_kallsyms_symbolizer_symbolize (SysprofSymbolizer *symbolizer, SysprofAddressContext context, SysprofAddress address) { - return NULL; + SysprofKallsymsSymbolizer *self = (SysprofKallsymsSymbolizer *)symbolizer; + const KernelSymbol *symbols; + guint n_symbols; + guint left; + guint right; + guint mid; + + if (context != SYSPROF_ADDRESS_CONTEXT_KERNEL) + return NULL; + + if (address < self->low || address >= self->high) + return NULL; + + symbols = &g_array_index (self->kallsyms, KernelSymbol, 0); + n_symbols = self->kallsyms->len; + + left = 0; + right = n_symbols; + mid = n_symbols / 2; + + for (;;) + { + const KernelSymbol *ksym = &symbols[mid]; + const KernelSymbol *next = &symbols[mid+1]; + + if (address >= ksym->address && + (address < next->address || next->address == 0)) + return _sysprof_symbol_new (g_ref_string_acquire (ksym->name), + g_ref_string_acquire (linux_string), + NULL, + ksym->address, + next->address ? next->address : ksym->address + 0xFFFF); + + if (address < ksym->address) + right = mid; + else + left = mid + 1; + + mid = left + ((right-left) / 2); + } + + g_assert_not_reached (); } static void @@ -224,12 +289,13 @@ sysprof_kallsyms_symbolizer_class_init (SysprofKallsymsSymbolizerClass *klass) symbolizer_class->symbolize = sysprof_kallsyms_symbolizer_symbolize; kallsym_strings = sysprof_strings_new (); + linux_string = g_ref_string_new_intern ("Linux"); } static void sysprof_kallsyms_symbolizer_init (SysprofKallsymsSymbolizer *self) { - self->kallsyms = g_array_new (FALSE, FALSE, sizeof (KernelSymbol)); + self->kallsyms = g_array_new (TRUE, FALSE, sizeof (KernelSymbol)); g_array_set_clear_func (self->kallsyms, kernel_symbol_clear); } From 909228174e7aef51b12c42645859867681485b4d Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 15 May 2023 13:51:22 -0700 Subject: [PATCH 0136/1030] libsysprof-analyze: use mapped file from file chunk --- src/libsysprof-analyze/sysprof-document-file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libsysprof-analyze/sysprof-document-file.c b/src/libsysprof-analyze/sysprof-document-file.c index fe7fd4cd..aa380b61 100644 --- a/src/libsysprof-analyze/sysprof-document-file.c +++ b/src/libsysprof-analyze/sysprof-document-file.c @@ -188,7 +188,7 @@ sysprof_document_file_read (SysprofDocumentFile *self) bytes = g_bytes_new_with_free_func (data, len, (GDestroyNotify)g_mapped_file_unref, - g_mapped_file_ref (SYSPROF_DOCUMENT_FRAME (self)->mapped_file)); + g_mapped_file_ref (SYSPROF_DOCUMENT_FRAME (file_chunk)->mapped_file)); g_memory_input_stream_add_bytes (G_MEMORY_INPUT_STREAM (input), bytes); } From a90b9a2fc72fb632e659e356c10ca38fab9ae62f Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 15 May 2023 14:30:06 -0700 Subject: [PATCH 0137/1030] libsysprof-analyze: use separate cache for kernel symbols Additionally this reduces some GHashTable lookup costs by doing it once for the process-info per-traceable rather than one per instruction pointer per traceable. --- .../sysprof-document-symbols-private.h | 26 +++---- .../sysprof-document-symbols.c | 75 ++++++++++--------- src/libsysprof-analyze/sysprof-document.c | 4 +- 3 files changed, 55 insertions(+), 50 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-document-symbols-private.h b/src/libsysprof-analyze/sysprof-document-symbols-private.h index 680b6468..e1b61625 100644 --- a/src/libsysprof-analyze/sysprof-document-symbols-private.h +++ b/src/libsysprof-analyze/sysprof-document-symbols-private.h @@ -31,18 +31,18 @@ G_BEGIN_DECLS G_DECLARE_FINAL_TYPE (SysprofDocumentSymbols, sysprof_document_symbols, SYSPROF, DOCUMENT_SYMBOLS, GObject) -void _sysprof_document_symbols_new (SysprofDocument *document, - SysprofStrings *strings, - SysprofSymbolizer *symbolizer, - GHashTable *pid_to_process_info, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -SysprofDocumentSymbols *_sysprof_document_symbols_new_finish (GAsyncResult *result, - GError **error); -SysprofSymbol *_sysprof_document_symbols_lookup (SysprofDocumentSymbols *symbols, - int pid, - SysprofAddressContext context, - SysprofAddress address); +void _sysprof_document_symbols_new (SysprofDocument *document, + SysprofStrings *strings, + SysprofSymbolizer *symbolizer, + GHashTable *pid_to_process_info, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +SysprofDocumentSymbols *_sysprof_document_symbols_new_finish (GAsyncResult *result, + GError **error); +SysprofSymbol *_sysprof_document_symbols_lookup (SysprofDocumentSymbols *symbols, + const SysprofProcessInfo *process_info, + SysprofAddressContext context, + SysprofAddress address); G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-document-symbols.c b/src/libsysprof-analyze/sysprof-document-symbols.c index 13a76d03..ff63de29 100644 --- a/src/libsysprof-analyze/sysprof-document-symbols.c +++ b/src/libsysprof-analyze/sysprof-document-symbols.c @@ -32,9 +32,9 @@ struct _SysprofDocumentSymbols { - GObject parent_instance; - SysprofSymbol *context_switches[SYSPROF_ADDRESS_CONTEXT_GUEST_USER+1]; - GHashTable *pid_to_process_info; + GObject parent_instance; + SysprofSymbol *context_switches[SYSPROF_ADDRESS_CONTEXT_GUEST_USER+1]; + SysprofSymbolCache *kernel_symbols; }; G_DEFINE_FINAL_TYPE (SysprofDocumentSymbols, sysprof_document_symbols, G_TYPE_OBJECT) @@ -47,8 +47,6 @@ sysprof_document_symbols_finalize (GObject *object) for (guint i = 0; i < G_N_ELEMENTS (self->context_switches); i++) g_clear_object (&self->context_switches[i]); - g_clear_pointer (&self->pid_to_process_info, g_hash_table_unref); - G_OBJECT_CLASS (sysprof_document_symbols_parent_class)->finalize (object); } @@ -63,6 +61,7 @@ sysprof_document_symbols_class_init (SysprofDocumentSymbolsClass *klass) static void sysprof_document_symbols_init (SysprofDocumentSymbols *self) { + self->kernel_symbols = sysprof_symbol_cache_new (); } typedef struct _Symbolize @@ -86,7 +85,8 @@ symbolize_free (Symbolize *state) } static void -add_traceable (SysprofStrings *strings, +add_traceable (SysprofDocumentSymbols *self, + SysprofStrings *strings, SysprofProcessInfo *process_info, SysprofDocumentTraceable *traceable, SysprofSymbolizer *symbolizer) @@ -95,7 +95,6 @@ add_traceable (SysprofStrings *strings, guint64 *addresses; guint n_addresses; - g_assert (process_info != NULL); g_assert (SYSPROF_IS_DOCUMENT_TRACEABLE (traceable)); g_assert (SYSPROF_IS_SYMBOLIZER (symbolizer)); @@ -113,16 +112,28 @@ add_traceable (SysprofStrings *strings, if (sysprof_address_is_context_switch (address, &context)) { last_context = context; - } - else if (sysprof_symbol_cache_lookup (process_info->symbol_cache, address) != NULL) - { continue; } + + if (last_context == SYSPROF_ADDRESS_CONTEXT_KERNEL) + { + g_autoptr(SysprofSymbol) symbol = NULL; + + if (sysprof_symbol_cache_lookup (self->kernel_symbols, address) != NULL) + continue; + + if ((symbol = _sysprof_symbolizer_symbolize (symbolizer, strings, process_info, last_context, address))) + sysprof_symbol_cache_take (self->kernel_symbols, g_steal_pointer (&symbol)); + } else { - g_autoptr(SysprofSymbol) symbol = _sysprof_symbolizer_symbolize (symbolizer, strings, process_info, last_context, address); + g_autoptr(SysprofSymbol) symbol = NULL; - if (symbol != NULL) + if (process_info != NULL && + sysprof_symbol_cache_lookup (process_info->symbol_cache, address) != NULL) + continue; + + if ((symbol = _sysprof_symbolizer_symbolize (symbolizer, strings, process_info, last_context, address))) sysprof_symbol_cache_take (process_info->symbol_cache, g_steal_pointer (&symbol)); } } @@ -185,21 +196,15 @@ sysprof_document_symbols_worker (GTask *task, int pid = sysprof_document_frame_get_pid (SYSPROF_DOCUMENT_FRAME (traceable)); SysprofProcessInfo *process_info = g_hash_table_lookup (state->pid_to_process_info, GINT_TO_POINTER (pid)); - /* We might hit this if we have "Process 0" which may be useful to - * let users know can take processing time. For now, that will just - * get skipped unless we deem it really valuable (you'll just jump - * to "- - Kernel - -" anyway. - */ - if (process_info == NULL) - continue; - - add_traceable (state->strings, process_info, traceable, state->symbolizer); + add_traceable (state->symbols, + state->strings, + process_info, + traceable, + state->symbolizer); } while (gtk_bitset_iter_next (&iter, &i)); } - state->symbols->pid_to_process_info = g_hash_table_ref (state->pid_to_process_info); - g_task_return_pointer (task, g_object_ref (state->symbols), g_object_unref); @@ -247,7 +252,7 @@ _sysprof_document_symbols_new_finish (GAsyncResult *result, /** * _sysprof_document_symbols_lookup: * @self: a #SysprofDocumentSymbols - * @pid: the process identifier + * @process_info: (nullable): the process info if necessary * @context: the #SysprofAddressContext for the address * @address: a #SysprofAddress to lookup the symbol for * @@ -256,26 +261,24 @@ _sysprof_document_symbols_new_finish (GAsyncResult *result, * Returns: (transfer none) (nullable): a #SysprofSymbol or %NULL */ SysprofSymbol * -_sysprof_document_symbols_lookup (SysprofDocumentSymbols *self, - int pid, - SysprofAddressContext context, - SysprofAddress address) +_sysprof_document_symbols_lookup (SysprofDocumentSymbols *self, + const SysprofProcessInfo *process_info, + SysprofAddressContext context, + SysprofAddress address) { SysprofAddressContext new_context; - SysprofProcessInfo *process_info; g_return_val_if_fail (SYSPROF_IS_DOCUMENT_SYMBOLS (self), NULL); g_return_val_if_fail (context <= SYSPROF_ADDRESS_CONTEXT_GUEST_USER, NULL); - /* TODO: Much better to do decoding in a group of addresses than - * one at a time, so should change this interface a bit. - */ - if (sysprof_address_is_context_switch (address, &new_context)) return self->context_switches[new_context]; - if (!(process_info = g_hash_table_lookup (self->pid_to_process_info, GINT_TO_POINTER (pid)))) - return NULL; + if (context == SYSPROF_ADDRESS_CONTEXT_KERNEL) + return sysprof_symbol_cache_lookup (self->kernel_symbols, address); - return sysprof_symbol_cache_lookup (process_info->symbol_cache, address); + if (process_info != NULL) + return sysprof_symbol_cache_lookup (process_info->symbol_cache, address); + + return NULL; } diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index 83384564..f5f2c8a3 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -802,6 +802,7 @@ sysprof_document_symbolize_traceable (SysprofDocument *self, guint n_symbols) { SysprofAddressContext last_context = SYSPROF_ADDRESS_CONTEXT_NONE; + const SysprofProcessInfo *process_info; SysprofAddress *addresses; guint n_addresses; int pid; @@ -813,6 +814,7 @@ sysprof_document_symbolize_traceable (SysprofDocument *self, return 0; pid = sysprof_document_frame_get_pid (SYSPROF_DOCUMENT_FRAME (traceable)); + process_info = g_hash_table_lookup (self->pid_to_process_info, GINT_TO_POINTER (pid)); addresses = g_alloca (sizeof (SysprofAddress) * n_symbols); n_addresses = sysprof_document_traceable_get_stack_addresses (traceable, addresses, n_symbols); @@ -820,7 +822,7 @@ sysprof_document_symbolize_traceable (SysprofDocument *self, { SysprofAddressContext context; - symbols[i] = _sysprof_document_symbols_lookup (self->symbols, pid, last_context, addresses[i]); + symbols[i] = _sysprof_document_symbols_lookup (self->symbols, process_info, last_context, addresses[i]); if (sysprof_address_is_context_switch (addresses[i], &context)) last_context = context; From 65318afa51452169078237709acafffbc04d8389 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 15 May 2023 14:30:29 -0700 Subject: [PATCH 0138/1030] libsysprof-analyze: use define for final address range --- src/libsysprof-analyze/sysprof-kallsyms-symbolizer.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-kallsyms-symbolizer.c b/src/libsysprof-analyze/sysprof-kallsyms-symbolizer.c index 7c4d0a78..c5186d8d 100644 --- a/src/libsysprof-analyze/sysprof-kallsyms-symbolizer.c +++ b/src/libsysprof-analyze/sysprof-kallsyms-symbolizer.c @@ -28,6 +28,8 @@ #include "sysprof-symbolizer-private.h" #include "sysprof-symbol-private.h" +#define LAST_SYMBOL_LEN 0xffff + typedef struct _KernelSymbol { guint64 address; @@ -157,7 +159,7 @@ sysprof_kallsyms_symbolizer_prepare_worker (GTask *task, const KernelSymbol *tail = &g_array_index (self->kallsyms, KernelSymbol, self->kallsyms->len-1); self->low = head->address; - self->high = tail->address + 0xFFFF; + self->high = tail->address + LAST_SYMBOL_LEN; } g_task_return_boolean (task, TRUE); @@ -253,7 +255,7 @@ sysprof_kallsyms_symbolizer_symbolize (SysprofSymbolizer *symbolizer, g_ref_string_acquire (linux_string), NULL, ksym->address, - next->address ? next->address : ksym->address + 0xFFFF); + next->address ? next->address : ksym->address + LAST_SYMBOL_LEN); if (address < ksym->address) right = mid; From 22828daad1a211dbec93533e7f05370459e971d6 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 15 May 2023 14:31:18 -0700 Subject: [PATCH 0139/1030] libsysprof-anzlyze: deduplicate and sort kernel address ranges Turns out we do need this, and we cannot trust kallsyms all that much even from duplicated entries on immediate next lines. --- .../sysprof-kallsyms-symbolizer.c | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-kallsyms-symbolizer.c b/src/libsysprof-analyze/sysprof-kallsyms-symbolizer.c index c5186d8d..b910169f 100644 --- a/src/libsysprof-analyze/sysprof-kallsyms-symbolizer.c +++ b/src/libsysprof-analyze/sysprof-kallsyms-symbolizer.c @@ -72,6 +72,21 @@ sysprof_kallsyms_symbolizer_add (SysprofKallsymsSymbolizer *self, g_array_append_val (self->kallsyms, s); } +static inline int +sort_by_address (gconstpointer a, + gconstpointer b) +{ + const KernelSymbol *sym_a = a; + const KernelSymbol *sym_b = b; + + if (sym_a->address < sym_b->address) + return -1; + else if (sym_a->address > sym_b->address) + return 1; + else + return 0; +} + static void sysprof_kallsyms_symbolizer_prepare_worker (GTask *task, gpointer source_object, @@ -132,11 +147,11 @@ sysprof_kallsyms_symbolizer_prepare_worker (GTask *task, /* Make @name usable as C string */ *iter = 0; - /* Only add this if we're after the previous address so that - * we can be sure our sort is proper and will not break the - * tight loop in lookup binary search. + /* Sometimes we get duplicates in kallsyms right after one another. + * Rather than try to deduplicate those all after they're in the + * array just detect the simple case and skip them now. */ - if (address > last_address) + if (address != last_address) sysprof_kallsyms_symbolizer_add (self, address, type, name); last_address = address; @@ -145,9 +160,10 @@ sysprof_kallsyms_symbolizer_prepare_worker (GTask *task, g_free (line); } - /* Symbols are already sorted in kallsyms, so no need to g_array_sort(). - * We just trust that the kernel did that part correctly. + /* We cannot rely on sorting of kallsyms up-front from Linux in all + * cases so we must sort the resulting array now. */ + g_array_sort (self->kallsyms, sort_by_address); /* Store a "best guess" at an lower/upper bound for the max address so that * we can avoid searching for anything unreasonably past the end of the last From 64a886eea838b5e6dcc3093d8b07dcdfdfa45d58 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 15 May 2023 14:33:41 -0700 Subject: [PATCH 0140/1030] libsysprof-analyze: handle NULL process info This can happen for "process 0" for example, or anything that was recorded for a pid which did not get a SysprofCaptureProcess frame. --- src/libsysprof-analyze/sysprof-bundled-symbolizer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libsysprof-analyze/sysprof-bundled-symbolizer.c b/src/libsysprof-analyze/sysprof-bundled-symbolizer.c index be28d8e7..b7052056 100644 --- a/src/libsysprof-analyze/sysprof-bundled-symbolizer.c +++ b/src/libsysprof-analyze/sysprof-bundled-symbolizer.c @@ -181,7 +181,7 @@ sysprof_bundled_symbolizer_symbolize (SysprofSymbolizer *symbolizer, const Decoded key = { .addr_begin = address, .addr_end = address, - .pid = process_info->pid, + .pid = process_info ? process_info->pid : 0, .offset = 0, .tag_offset = 0, }; From d415f29a15ee635ca1ebd18a611179ab6f5c3f5c Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 15 May 2023 14:52:59 -0700 Subject: [PATCH 0141/1030] libsysprof-analyze: allow disabling bundled symbolizer --- src/libsysprof-analyze/tests/test-symbolize.c | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/libsysprof-analyze/tests/test-symbolize.c b/src/libsysprof-analyze/tests/test-symbolize.c index 29e5e3ef..0eed4465 100644 --- a/src/libsysprof-analyze/tests/test-symbolize.c +++ b/src/libsysprof-analyze/tests/test-symbolize.c @@ -96,14 +96,29 @@ load_cb (GObject *object, g_main_loop_quit (main_loop); } +static gboolean no_bundled; +static const GOptionEntry entries[] = { + { "no-bundled", 'b', 0, G_OPTION_ARG_NONE, &no_bundled, "Ignore symbols bundled in capture file" }, + { 0 } +}; + int main (int argc, char *argv[]) { g_autoptr(SysprofDocumentLoader) loader = NULL; + g_autoptr(GOptionContext) context = NULL; g_autoptr(GError) error = NULL; main_loop = g_main_loop_new (NULL, FALSE); + context = g_option_context_new ("- test document symbolization"); + g_option_context_add_main_entries (context, entries, NULL); + + if (!g_option_context_parse (context, &argc, &argv, &error)) + { + g_printerr ("%s\n", error->message); + return 1; + } if (argc != 2 || !g_file_test (argv[1], G_FILE_TEST_EXISTS)) { @@ -112,6 +127,17 @@ main (int argc, } loader = sysprof_document_loader_new (argv[1]); + + if (no_bundled) + { + g_autoptr(SysprofMultiSymbolizer) multi = sysprof_multi_symbolizer_new (); + + //sysprof_multi_symbolizer_take (multi, sysprof_bundled_symbolizer_new ()); + sysprof_multi_symbolizer_take (multi, sysprof_kallsyms_symbolizer_new ()); + + sysprof_document_loader_set_symbolizer (loader, SYSPROF_SYMBOLIZER (multi)); + } + sysprof_document_loader_load_async (loader, NULL, load_cb, NULL); g_main_loop_run (main_loop); From 140bc1d3848ac428d36fcfc01aca5c31b29ae032 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 15 May 2023 15:03:25 -0700 Subject: [PATCH 0142/1030] libsysprof-analyze: add scaffolding for elf symbol resolver --- src/libsysprof-analyze/meson.build | 2 + src/libsysprof-analyze/sysprof-analyze.h | 1 + .../sysprof-elf-symbolizer.c | 83 +++++++++++++++++++ .../sysprof-elf-symbolizer.h | 42 ++++++++++ src/libsysprof-analyze/tests/test-symbolize.c | 2 +- 5 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 src/libsysprof-analyze/sysprof-elf-symbolizer.c create mode 100644 src/libsysprof-analyze/sysprof-elf-symbolizer.h diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build index ba11a216..c0508e62 100644 --- a/src/libsysprof-analyze/meson.build +++ b/src/libsysprof-analyze/meson.build @@ -15,6 +15,7 @@ libsysprof_analyze_public_sources = [ 'sysprof-document-process.c', 'sysprof-document-sample.c', 'sysprof-document-traceable.c', + 'sysprof-elf-symbolizer.c', 'sysprof-kallsyms-symbolizer.c', 'sysprof-multi-symbolizer.c', 'sysprof-no-symbolizer.c', @@ -52,6 +53,7 @@ libsysprof_analyze_public_headers = [ 'sysprof-document-process.h', 'sysprof-document-sample.h', 'sysprof-document-traceable.h', + 'sysprof-elf-symbolizer.h', 'sysprof-mount.h', 'sysprof-kallsyms-symbolizer.h', 'sysprof-multi-symbolizer.h', diff --git a/src/libsysprof-analyze/sysprof-analyze.h b/src/libsysprof-analyze/sysprof-analyze.h index 0461cd82..b1fb7b39 100644 --- a/src/libsysprof-analyze/sysprof-analyze.h +++ b/src/libsysprof-analyze/sysprof-analyze.h @@ -41,6 +41,7 @@ G_BEGIN_DECLS # include "sysprof-document-process.h" # include "sysprof-document-sample.h" # include "sysprof-document-traceable.h" +# include "sysprof-elf-symbolizer.h" # include "sysprof-mount.h" # include "sysprof-kallsyms-symbolizer.h" # include "sysprof-multi-symbolizer.h" diff --git a/src/libsysprof-analyze/sysprof-elf-symbolizer.c b/src/libsysprof-analyze/sysprof-elf-symbolizer.c new file mode 100644 index 00000000..39d9acec --- /dev/null +++ b/src/libsysprof-analyze/sysprof-elf-symbolizer.c @@ -0,0 +1,83 @@ +/* sysprof-elf-symbolizer.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-elf-symbolizer.h" +#include "sysprof-document-private.h" +#include "sysprof-strings-private.h" +#include "sysprof-symbolizer-private.h" +#include "sysprof-symbol-private.h" + +struct _SysprofElfSymbolizer +{ + SysprofSymbolizer parent_instance; +}; + +struct _SysprofElfSymbolizerClass +{ + SysprofSymbolizerClass parent_class; +}; + +G_DEFINE_FINAL_TYPE (SysprofElfSymbolizer, sysprof_elf_symbolizer, SYSPROF_TYPE_SYMBOLIZER) + +static SysprofSymbol * +sysprof_elf_symbolizer_symbolize (SysprofSymbolizer *symbolizer, + SysprofStrings *strings, + const SysprofProcessInfo *process_info, + SysprofAddressContext context, + SysprofAddress address) +{ + if (context != SYSPROF_ADDRESS_CONTEXT_NONE && + context != SYSPROF_ADDRESS_CONTEXT_USER) + return NULL; + + return NULL; +} + +static void +sysprof_elf_symbolizer_finalize (GObject *object) +{ + SysprofElfSymbolizer *self = (SysprofElfSymbolizer *)object; + + G_OBJECT_CLASS (sysprof_elf_symbolizer_parent_class)->finalize (object); +} + +static void +sysprof_elf_symbolizer_class_init (SysprofElfSymbolizerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + SysprofSymbolizerClass *symbolizer_class = SYSPROF_SYMBOLIZER_CLASS (klass); + + object_class->finalize = sysprof_elf_symbolizer_finalize; + + symbolizer_class->symbolize = sysprof_elf_symbolizer_symbolize; +} + +static void +sysprof_elf_symbolizer_init (SysprofElfSymbolizer *self) +{ +} + +SysprofSymbolizer * +sysprof_elf_symbolizer_new (void) +{ + return g_object_new (SYSPROF_TYPE_ELF_SYMBOLIZER, NULL); +} diff --git a/src/libsysprof-analyze/sysprof-elf-symbolizer.h b/src/libsysprof-analyze/sysprof-elf-symbolizer.h new file mode 100644 index 00000000..99259f0d --- /dev/null +++ b/src/libsysprof-analyze/sysprof-elf-symbolizer.h @@ -0,0 +1,42 @@ +/* sysprof-elf-symbolizer.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-symbolizer.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_ELF_SYMBOLIZER (sysprof_elf_symbolizer_get_type()) +#define SYSPROF_IS_ELF_SYMBOLIZER(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_ELF_SYMBOLIZER) +#define SYSPROF_ELF_SYMBOLIZER(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_ELF_SYMBOLIZER, SysprofElfSymbolizer) +#define SYSPROF_ELF_SYMBOLIZER_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_ELF_SYMBOLIZER, SysprofElfSymbolizerClass) + +typedef struct _SysprofElfSymbolizer SysprofElfSymbolizer; +typedef struct _SysprofElfSymbolizerClass SysprofElfSymbolizerClass; + +SYSPROF_AVAILABLE_IN_ALL +GType sysprof_elf_symbolizer_get_type (void) G_GNUC_CONST; +SYSPROF_AVAILABLE_IN_ALL +SysprofSymbolizer *sysprof_elf_symbolizer_new (void); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofElfSymbolizer, g_object_unref) + +G_END_DECLS diff --git a/src/libsysprof-analyze/tests/test-symbolize.c b/src/libsysprof-analyze/tests/test-symbolize.c index 0eed4465..c3850088 100644 --- a/src/libsysprof-analyze/tests/test-symbolize.c +++ b/src/libsysprof-analyze/tests/test-symbolize.c @@ -132,7 +132,7 @@ main (int argc, { g_autoptr(SysprofMultiSymbolizer) multi = sysprof_multi_symbolizer_new (); - //sysprof_multi_symbolizer_take (multi, sysprof_bundled_symbolizer_new ()); + sysprof_multi_symbolizer_take (multi, sysprof_elf_symbolizer_new ()); sysprof_multi_symbolizer_take (multi, sysprof_kallsyms_symbolizer_new ()); sysprof_document_loader_set_symbolizer (loader, SYSPROF_SYMBOLIZER (multi)); From b786edd848c42f34a2a6b7080791676a1c0b5674 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 15 May 2023 15:20:52 -0700 Subject: [PATCH 0143/1030] libsysprof-analyze: print mmap information --- src/libsysprof-analyze/tests/test-capture-model.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/libsysprof-analyze/tests/test-capture-model.c b/src/libsysprof-analyze/tests/test-capture-model.c index ee4252b4..495f65d5 100644 --- a/src/libsysprof-analyze/tests/test-capture-model.c +++ b/src/libsysprof-analyze/tests/test-capture-model.c @@ -70,6 +70,12 @@ main (int argc, else if (SYSPROF_IS_DOCUMENT_FORK (frame)) g_print (" child-pid=%d", sysprof_document_fork_get_child_pid (SYSPROF_DOCUMENT_FORK (frame))); + else if (SYSPROF_IS_DOCUMENT_MMAP (frame)) + g_print (" begin=0x%"G_GINT64_MODIFIER"x end=0x%"G_GINT64_MODIFIER"x offset=0x%"G_GINT64_MODIFIER"x path=%s", + sysprof_document_mmap_get_start_address (SYSPROF_DOCUMENT_MMAP (frame)), + sysprof_document_mmap_get_end_address (SYSPROF_DOCUMENT_MMAP (frame)), + sysprof_document_mmap_get_file_offset (SYSPROF_DOCUMENT_MMAP (frame)), + sysprof_document_mmap_get_file (SYSPROF_DOCUMENT_MMAP (frame))); else if (SYSPROF_IS_DOCUMENT_ALLOCATION (frame)) { if (sysprof_document_allocation_is_free (SYSPROF_DOCUMENT_ALLOCATION (frame))) From 287c7feca2a600c8d4786a50d867d98a4093e513 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 15 May 2023 15:26:27 -0700 Subject: [PATCH 0144/1030] libsysprof-analyze: add test to analyze address layout --- src/libsysprof-analyze/tests/meson.build | 13 +-- .../tests/test-list-address-layout.c | 80 +++++++++++++++++++ 2 files changed, 87 insertions(+), 6 deletions(-) create mode 100644 src/libsysprof-analyze/tests/test-list-address-layout.c diff --git a/src/libsysprof-analyze/tests/meson.build b/src/libsysprof-analyze/tests/meson.build index 1e161e3a..1126df96 100644 --- a/src/libsysprof-analyze/tests/meson.build +++ b/src/libsysprof-analyze/tests/meson.build @@ -12,12 +12,13 @@ libsysprof_analyze_testsuite_c_args = [ ] libsysprof_analyze_testsuite = { - 'test-capture-model' : {'skip': true}, - 'test-list-files' : {'skip': true}, - 'test-print-file' : {'skip': true}, - 'test-list-processes' : {'skip': true}, - 'test-symbolize' : {'skip': true}, - 'test-symbol-cache' : {}, + 'test-capture-model' : {'skip': true}, + 'test-list-files' : {'skip': true}, + 'test-print-file' : {'skip': true}, + 'test-list-processes' : {'skip': true}, + 'test-list-address-layout' : {'skip': true}, + 'test-symbolize' : {'skip': true}, + 'test-symbol-cache' : {}, } libsysprof_analyze_testsuite_deps = [ diff --git a/src/libsysprof-analyze/tests/test-list-address-layout.c b/src/libsysprof-analyze/tests/test-list-address-layout.c new file mode 100644 index 00000000..2881d22d --- /dev/null +++ b/src/libsysprof-analyze/tests/test-list-address-layout.c @@ -0,0 +1,80 @@ +/* test-list-address-layout.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include + +#include "sysprof-document-private.h" + +int +main (int argc, + char *argv[]) +{ + g_autoptr(SysprofDocumentLoader) loader = NULL; + g_autoptr(SysprofDocument) document = NULL; + g_autoptr(GListModel) processes = NULL; + g_autoptr(GError) error = NULL; + guint n_items; + int pid; + + if (argc < 3) + { + g_printerr ("usage: %s CAPTURE_FILE PID\n", argv[0]); + return 1; + } + + pid = atoi (argv[2]); + loader = sysprof_document_loader_new (argv[1]); + sysprof_document_loader_set_symbolizer (loader, sysprof_no_symbolizer_get ()); + + if (!(document = sysprof_document_loader_load (loader, NULL, &error))) + { + g_printerr ("Failed to open capture: %s\n", error->message); + return 1; + } + + processes = sysprof_document_list_processes (document); + n_items = g_list_model_get_n_items (processes); + + for (guint i = 0; i < n_items; i++) + { + g_autoptr(SysprofDocumentProcess) process = g_list_model_get_item (processes, i); + + if (sysprof_document_frame_get_pid (SYSPROF_DOCUMENT_FRAME (process)) == pid) + { + g_autoptr(GListModel) memory_maps = sysprof_document_process_list_memory_maps (process); + guint n_maps = g_list_model_get_n_items (memory_maps); + + for (guint j = 0; j < n_maps; j++) + { + g_autoptr(SysprofDocumentMmap) map = g_list_model_get_item (memory_maps, j); + + g_print (" [0x%"G_GINT64_MODIFIER"x:0x%"G_GINT64_MODIFIER"x] %s <+0x%"G_GINT64_MODIFIER"x>\n", + sysprof_document_mmap_get_start_address (map), + sysprof_document_mmap_get_end_address (map), + sysprof_document_mmap_get_file (map), + sysprof_document_mmap_get_file_offset (map)); + } + + break; + } + } + + return 0; +} From 09b8c1f7378278ff299046c5bfa65d5d88c09467 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 15 May 2023 16:49:37 -0700 Subject: [PATCH 0145/1030] libsysprof-analyze: leave some notes on elf symbolizing --- src/libsysprof-analyze/meson.build | 3 + .../sysprof-elf-symbolizer.c | 85 ++++++++++++++++++- 2 files changed, 84 insertions(+), 4 deletions(-) diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build index c0508e62..a03e26f0 100644 --- a/src/libsysprof-analyze/meson.build +++ b/src/libsysprof-analyze/meson.build @@ -24,6 +24,9 @@ libsysprof_analyze_public_sources = [ ] libsysprof_analyze_private_sources = [ + '../libsysprof/binfile.c', + '../libsysprof/demangle.cpp', + '../libsysprof/elfparser.c', 'sysprof-address-layout.c', 'sysprof-document-bitset-index.c', 'sysprof-document-symbols.c', diff --git a/src/libsysprof-analyze/sysprof-elf-symbolizer.c b/src/libsysprof-analyze/sysprof-elf-symbolizer.c index 39d9acec..121375f3 100644 --- a/src/libsysprof-analyze/sysprof-elf-symbolizer.c +++ b/src/libsysprof-analyze/sysprof-elf-symbolizer.c @@ -20,6 +20,8 @@ #include "config.h" +#include "../libsysprof/binfile.h" + #include "sysprof-elf-symbolizer.h" #include "sysprof-document-private.h" #include "sysprof-strings-private.h" @@ -28,7 +30,8 @@ struct _SysprofElfSymbolizer { - SysprofSymbolizer parent_instance; + SysprofSymbolizer parent_instance; + GHashTable *bin_file_cache; }; struct _SysprofElfSymbolizerClass @@ -45,11 +48,79 @@ sysprof_elf_symbolizer_symbolize (SysprofSymbolizer *symbolizer, SysprofAddressContext context, SysprofAddress address) { - if (context != SYSPROF_ADDRESS_CONTEXT_NONE && - context != SYSPROF_ADDRESS_CONTEXT_USER) + SysprofElfSymbolizer *self = (SysprofElfSymbolizer *)symbolizer; + SysprofDocumentMmap *map; + g_autofree char *name = NULL; + g_auto(GStrv) translations = NULL; + GMappedFile *mapped_file = NULL; + const char *path; + + if (process_info == NULL || + process_info->address_layout == NULL || + process_info->mount_namespace == NULL || + (context != SYSPROF_ADDRESS_CONTEXT_NONE && + context != SYSPROF_ADDRESS_CONTEXT_USER)) return NULL; - return NULL; + /* First find out what was mapped at that address */ + if (!(map = sysprof_address_layout_lookup (process_info->address_layout, address))) + return NULL; + + /* The file could be available at a number of locations in case there + * is an overlayfs, flatpak runtime, etc. Additionally, we may need + * to resolve through various debug directories. All of those also + * need to get translated to a location where this process can access + * then (which itself might be via /var/run/host/... or similar). + */ + path = sysprof_document_mmap_get_file (map); + + /* TODO: + * + * We need something like bin_file_t here that will let us look at + * all of our possible translations for the file (overlayfs, etc) + * and add debug directories on top of that. The debug directories + * can be used to follow .gnu_debuglink through debug dirs. + */ + +#if 0 + if (!(translations = sysprof_mount_namespace_translate (process_info->mount_namespace, path))) + goto fallback; + + for (guint i = 0; translations[i]; i++) + { + /* If the file exists within our cache already, re-use that instead + * of re-opening a binfile. + */ + if ((mapped_file = g_hash_table_lookup (self->bin_file_cache, translations[i]))) + break; + + if ((mapped_file = g_mapped_file_new (translations[i], FALSE, NULL))) + { + g_hash_table_insert (self->bin_file_cache, + g_strdup (translations[i]), + mapped_file); + break; + } + } + + if (mapped_file == NULL) + goto fallback; +#endif + +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 the offset of the ELF section mapped. + */ + name = g_strdup_printf ("In file %s <+0x%"G_GINT64_MODIFIER"x>", + sysprof_document_mmap_get_file (map), + sysprof_document_mmap_get_file_offset (map)); + + return _sysprof_symbol_new (sysprof_strings_get (strings, name), + NULL, + NULL, + sysprof_document_mmap_get_start_address (map), + sysprof_document_mmap_get_end_address (map)); } static void @@ -57,6 +128,8 @@ sysprof_elf_symbolizer_finalize (GObject *object) { SysprofElfSymbolizer *self = (SysprofElfSymbolizer *)object; + g_clear_pointer (&self->bin_file_cache, g_hash_table_unref); + G_OBJECT_CLASS (sysprof_elf_symbolizer_parent_class)->finalize (object); } @@ -74,6 +147,10 @@ sysprof_elf_symbolizer_class_init (SysprofElfSymbolizerClass *klass) static void sysprof_elf_symbolizer_init (SysprofElfSymbolizer *self) { + self->bin_file_cache = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + (GDestroyNotify)bin_file_free); } SysprofSymbolizer * From 6d86d55d615e02f1f9a252d15ec6339e5a253cf1 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 16 May 2023 12:43:33 -0700 Subject: [PATCH 0146/1030] libsysprof-analyze: give address in file/function This at least gives more visibility into what location of the file is being executed. That way you can separate different parts of the file rather than one giant "this file" so long as we could unwind successfully. --- src/libsysprof-analyze/sysprof-elf-symbolizer.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-elf-symbolizer.c b/src/libsysprof-analyze/sysprof-elf-symbolizer.c index 121375f3..fd1aa257 100644 --- a/src/libsysprof-analyze/sysprof-elf-symbolizer.c +++ b/src/libsysprof-analyze/sysprof-elf-symbolizer.c @@ -53,6 +53,7 @@ sysprof_elf_symbolizer_symbolize (SysprofSymbolizer *symbolizer, g_autofree char *name = NULL; g_auto(GStrv) translations = NULL; GMappedFile *mapped_file = NULL; + guint64 relative_address; const char *path; if (process_info == NULL || @@ -110,17 +111,19 @@ sysprof_elf_symbolizer_symbolize (SysprofSymbolizer *symbolizer, 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 the offset of the ELF section mapped. + * and where (relative to that file) the IP was. */ - name = g_strdup_printf ("In file %s <+0x%"G_GINT64_MODIFIER"x>", + relative_address = sysprof_document_mmap_get_file_offset (map) + + (address - sysprof_document_mmap_get_start_address (map)); + name = g_strdup_printf ("In file %s+0x%"G_GINT64_MODIFIER"x", sysprof_document_mmap_get_file (map), - sysprof_document_mmap_get_file_offset (map)); + relative_address); return _sysprof_symbol_new (sysprof_strings_get (strings, name), NULL, NULL, - sysprof_document_mmap_get_start_address (map), - sysprof_document_mmap_get_end_address (map)); + address, + address + 1); } static void From 6ad94c59bf4296a96c2c953ab3611fa2027aef58 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 18 May 2023 11:49:21 -0700 Subject: [PATCH 0147/1030] libsysprof-analyze: add strv helpers --- .../sysprof-strings-private.h | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-strings-private.h b/src/libsysprof-analyze/sysprof-strings-private.h index 3d58b927..a4faae2d 100644 --- a/src/libsysprof-analyze/sysprof-strings-private.h +++ b/src/libsysprof-analyze/sysprof-strings-private.h @@ -34,4 +34,26 @@ GRefString *sysprof_strings_get (SysprofStrings *self, GRefString *sysprof_strings_lookup (SysprofStrings *self, const char *string); +#define SYSPROF_STRV_INIT(...) ((const char * const[]){__VA_ARGS__,NULL}) + +static inline gboolean +sysprof_set_strv (char ***dest, + const char * const *src) +{ + if ((const char * const *)*dest == src) + return FALSE; + + if (*dest == NULL || + src == NULL || + !g_strv_equal ((const char * const *)*dest, src)) + { + char **copy = g_strdupv ((char **)src); + g_strfreev (*dest); + *dest = copy; + return TRUE; + } + + return FALSE; +} + G_END_DECLS From 19c5a21ac0e536f526a3b0754e399fc4f3a72a5b Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 18 May 2023 12:00:25 -0700 Subject: [PATCH 0148/1030] libsysprof-analyze: give shape to ELF loading APIs We need to separate a number of concerns here, such as debug-dirs within the process namespace vs global debug-dirs (external symbols on a developer workstation vs IoT/Laptop/VM/alternate-device). That means we need to be able to resolve paths via the mount namespace of the process as it was seen in the capture file. Additionally, we need to follow .gnu_debuglink section headers so that we can associate the version with symbols with the ELF that is loaded from the processes SysprofAddressLayout. --- src/libsysprof-analyze/meson.build | 2 + .../sysprof-elf-loader-private.h | 49 ++++ src/libsysprof-analyze/sysprof-elf-loader.c | 258 ++++++++++++++++++ src/libsysprof-analyze/sysprof-elf-private.h | 43 +++ src/libsysprof-analyze/sysprof-elf.c | 201 ++++++++++++++ 5 files changed, 553 insertions(+) create mode 100644 src/libsysprof-analyze/sysprof-elf-loader-private.h create mode 100644 src/libsysprof-analyze/sysprof-elf-loader.c create mode 100644 src/libsysprof-analyze/sysprof-elf-private.h create mode 100644 src/libsysprof-analyze/sysprof-elf.c diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build index a03e26f0..7f9e4197 100644 --- a/src/libsysprof-analyze/meson.build +++ b/src/libsysprof-analyze/meson.build @@ -30,6 +30,8 @@ libsysprof_analyze_private_sources = [ 'sysprof-address-layout.c', 'sysprof-document-bitset-index.c', 'sysprof-document-symbols.c', + 'sysprof-elf.c', + 'sysprof-elf-loader.c', 'sysprof-mount.c', 'sysprof-mount-device.c', 'sysprof-mount-namespace.c', diff --git a/src/libsysprof-analyze/sysprof-elf-loader-private.h b/src/libsysprof-analyze/sysprof-elf-loader-private.h new file mode 100644 index 00000000..bfc0d74b --- /dev/null +++ b/src/libsysprof-analyze/sysprof-elf-loader-private.h @@ -0,0 +1,49 @@ +/* + * sysprof-elf-loader-private.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +#include "sysprof-elf-private.h" +#include "sysprof-mount-namespace-private.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_ELF_LOADER (sysprof_elf_loader_get_type()) + +G_DECLARE_FINAL_TYPE (SysprofElfLoader, sysprof_elf_loader, SYSPROF, ELF_LOADER, GObject) + +SysprofElfLoader *sysprof_elf_loader_new (void); +const char * const *sysprof_elf_loader_get_debug_dirs (SysprofElfLoader *self); +void sysprof_elf_loader_set_debug_dirs (SysprofElfLoader *self, + const char * const *debug_dirs); +const char * const *sysprof_elf_loader_get_external_debug_dirs (SysprofElfLoader *self); +void sysprof_elf_loader_set_external_debug_dirs (SysprofElfLoader *self, + const char * const *debug_dirs); +SysprofElf *sysprof_elf_loader_load (SysprofElfLoader *self, + SysprofMountNamespace *mount_namespace, + const char *file, + const char *build_id, + GError **error); + + +G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-elf-loader.c b/src/libsysprof-analyze/sysprof-elf-loader.c new file mode 100644 index 00000000..2a75b918 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-elf-loader.c @@ -0,0 +1,258 @@ +/* sysprof-elf-loader.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-elf-loader-private.h" +#include "sysprof-strings-private.h" + +#define DEFAULT_DEBUG_DIRS SYSPROF_STRV_INIT("/usr/lib/debug") + +struct _SysprofElfLoader +{ + GObject parent_instance; + char **debug_dirs; + char **external_debug_dirs; +}; + +enum { + PROP_0, + PROP_DEBUG_DIRS, + PROP_EXTERNAL_DEBUG_DIRS, + N_PROPS +}; + +G_DEFINE_FINAL_TYPE (SysprofElfLoader, sysprof_elf_loader, G_TYPE_OBJECT) + +static GParamSpec *properties [N_PROPS]; + +static void +sysprof_elf_loader_finalize (GObject *object) +{ + SysprofElfLoader *self = (SysprofElfLoader *)object; + + g_clear_pointer (&self->debug_dirs, g_strfreev); + g_clear_pointer (&self->external_debug_dirs, g_strfreev); + + G_OBJECT_CLASS (sysprof_elf_loader_parent_class)->finalize (object); +} + +static void +sysprof_elf_loader_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofElfLoader *self = SYSPROF_ELF_LOADER (object); + + switch (prop_id) + { + case PROP_DEBUG_DIRS: + g_value_set_boxed (value, sysprof_elf_loader_get_debug_dirs (self)); + break; + + case PROP_EXTERNAL_DEBUG_DIRS: + g_value_set_boxed (value, sysprof_elf_loader_get_external_debug_dirs (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_elf_loader_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SysprofElfLoader *self = SYSPROF_ELF_LOADER (object); + + switch (prop_id) + { + case PROP_DEBUG_DIRS: + sysprof_elf_loader_set_debug_dirs (self, g_value_get_boxed (value)); + break; + + case PROP_EXTERNAL_DEBUG_DIRS: + sysprof_elf_loader_set_external_debug_dirs (self, g_value_get_boxed (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_elf_loader_class_init (SysprofElfLoaderClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = sysprof_elf_loader_finalize; + object_class->get_property = sysprof_elf_loader_get_property; + object_class->set_property = sysprof_elf_loader_set_property; + + properties [PROP_DEBUG_DIRS] = + g_param_spec_boxed ("debug-dirs", NULL, NULL, + G_TYPE_STRV, + (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); + + properties [PROP_EXTERNAL_DEBUG_DIRS] = + g_param_spec_boxed ("external-debug-dirs", NULL, NULL, + G_TYPE_STRV, + (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); +} + +static void +sysprof_elf_loader_init (SysprofElfLoader *self) +{ + self->debug_dirs = g_strdupv ((char **)DEFAULT_DEBUG_DIRS); +} + +/** + * sysprof_elf_loader_get_debug_dirs: + * @self: a #SysprofElfLoader + * + * Gets the #SysprofElfLoader:debug-dirs property. + * + * See sysprof_elf_loader_set_debug_dirs() for information on how + * these directories are used. + * + * Returns: (nullable): an array of debug directories, or %NULL + */ +const char * const * +sysprof_elf_loader_get_debug_dirs (SysprofElfLoader *self) +{ + g_return_val_if_fail (SYSPROF_IS_ELF_LOADER (self), NULL); + + return (const char * const *)self->debug_dirs; +} + +/** + * sysprof_elf_loader_set_debug_dirs: + * @self: a #SysprofElfLoader + * @debug_dirs: the new debug directories to use + * + * Sets the #SysprofElfLoader:debug-dirs prpoerty. + * + * If @debug_dirs is %NULL, the default debug directories will + * be used. + * + * These directories will be used to resolve debug symbols within + * the mount namespace of the process whose symbols are being + * resolved. + * + * To set a global directory that may contain debug symbols, use + * sysprof_elf_loader_set_external_debug_dirs() which can be searched + * regardless of what mount namespace the resolving process was in. + */ +void +sysprof_elf_loader_set_debug_dirs (SysprofElfLoader *self, + const char * const *debug_dirs) +{ + g_return_if_fail (SYSPROF_IS_ELF_LOADER (self)); + g_return_if_fail (self->debug_dirs != NULL); + + if (sysprof_set_strv (&self->debug_dirs, debug_dirs)) + g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_DEBUG_DIRS]); +} + +/** + * sysprof_elf_loader_get_external_debug_dirs: + * @self: a #SysprofElfLoader + * + * Gets the #SysprofElfLoader:external-debug-dirs property. + * + * See sysprof_elf_loader_set_external_debug_dirs() for how this + * property is used to locate ELF files. + * + * Returns: (nullable): an array of external debug directories, or %NULL + */ +const char * const * +sysprof_elf_loader_get_external_debug_dirs (SysprofElfLoader *self) +{ + g_return_val_if_fail (SYSPROF_IS_ELF_LOADER (self), NULL); + + return (const char * const *)self->external_debug_dirs; +} + +/** + * sysprof_elf_loader_set_external_debug_dirs: + * @self: a #SysprofElfLoader + * @external_debug_dirs: (nullable): array of debug directories to resolve + * `.gnu_debuglink` references in ELF files. + * + * Sets the #SysprofElfLoader:external-debug-dirs property. + * + * This is used to resolve `.gnu_debuglink` files across any process that is + * loading ELF files with this #SysprofElfLoader. That allows for symbolizing + * a capture file that was recording on a different system for which the + * debug symbols only exist locally. + * + * Note that this has no effect on stack trace unwinding, that must occur + * independently of this, when the samples are recorded by the specific + * unwinder in use. + */ +void +sysprof_elf_loader_set_external_debug_dirs (SysprofElfLoader *self, + const char * const *external_debug_dirs) +{ + g_return_if_fail (SYSPROF_IS_ELF_LOADER (self)); + + if (sysprof_set_strv (&self->external_debug_dirs, external_debug_dirs)) + g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_EXTERNAL_DEBUG_DIRS]); +} + +/** + * sysprof_elf_loader_load: + * @self: a #SysprofElfLoader + * @mount_namespace: a #SysprofMountNamespace for path resolving + * @file: the path of the file to load within the mount namespace + * @build_id: (nullable): an optional build-id that can be used to resolve + * the file alternatively to the file path + * + * Attempts to load a #SysprofElf for @file (or optionally by @build_id). + * + * This attempts to follow `.gnu_debuglink` ELF section headers and attach + * them to the resulting #SysprofElf so that additional symbol information + * is available. + * + * Returns: (transfer full): a #SysprofElf, or %NULL if the file could + * not be resolved. + */ +SysprofElf * +sysprof_elf_loader_load (SysprofElfLoader *self, + SysprofMountNamespace *mount_namespace, + const char *file, + const char *build_id, + GError **error) +{ + g_return_val_if_fail (SYSPROF_IS_ELF_LOADER (self), NULL); + +failure: + g_set_error_literal (error, + G_FILE_ERROR, + G_FILE_ERROR_NOENT, + "Failed to locate file"); + + return NULL; +} diff --git a/src/libsysprof-analyze/sysprof-elf-private.h b/src/libsysprof-analyze/sysprof-elf-private.h new file mode 100644 index 00000000..eb99edb4 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-elf-private.h @@ -0,0 +1,43 @@ +/* sysprof-elf-private.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_ELF (sysprof_elf_get_type()) + +G_DECLARE_FINAL_TYPE (SysprofElf, sysprof_elf, SYSPROF, ELF, GObject) + +SysprofElf *sysprof_elf_new (const char *filename, + GError **error); +const char *sysprof_elf_get_build_id (SysprofElf *self); +const char *sysprof_elf_get_debug_link (SysprofElf *self); +const char *sysprof_elf_get_symbol_at_address (SysprofElf *self, + guint64 address, + guint64 *begin_address, + 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); + +G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-elf.c b/src/libsysprof-analyze/sysprof-elf.c new file mode 100644 index 00000000..c3662479 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-elf.c @@ -0,0 +1,201 @@ +/* sysprof-elf.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-elf-private.h" + +struct _SysprofElf +{ + GObject parent_instance; + char *build_id; + char *debug_link; + SysprofElf *debug_link_elf; +}; + +enum { + PROP_0, + PROP_BUILD_ID, + PROP_DEBUG_LINK, + PROP_DEBUG_LINK_ELF, + N_PROPS +}; + +G_DEFINE_FINAL_TYPE (SysprofElf, sysprof_elf, G_TYPE_OBJECT) + +static GParamSpec *properties [N_PROPS]; + +static void +sysprof_elf_finalize (GObject *object) +{ + SysprofElf *self = (SysprofElf *)object; + + g_clear_pointer (&self->build_id, g_free); + g_clear_pointer (&self->debug_link, g_free); + g_clear_object (&self->debug_link_elf); + + G_OBJECT_CLASS (sysprof_elf_parent_class)->finalize (object); +} + +static void +sysprof_elf_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofElf *self = SYSPROF_ELF (object); + + switch (prop_id) + { + case PROP_BUILD_ID: + g_value_set_string (value, sysprof_elf_get_build_id (self)); + break; + + case PROP_DEBUG_LINK: + g_value_set_string (value, sysprof_elf_get_debug_link (self)); + break; + + case PROP_DEBUG_LINK_ELF: + g_value_set_object (value, sysprof_elf_get_debug_link_elf (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_elf_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SysprofElf *self = SYSPROF_ELF (object); + + switch (prop_id) + { + case PROP_DEBUG_LINK_ELF: + sysprof_elf_set_debug_link_elf (self, g_value_get_object (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_elf_class_init (SysprofElfClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = sysprof_elf_finalize; + object_class->get_property = sysprof_elf_get_property; + object_class->set_property = sysprof_elf_set_property; + + properties [PROP_BUILD_ID] = + g_param_spec_string ("build-id", NULL, NULL, + NULL, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + properties [PROP_DEBUG_LINK] = + g_param_spec_string ("debug-link", NULL, NULL, + NULL, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + properties [PROP_DEBUG_LINK_ELF] = + g_param_spec_object ("debug-link-elf", NULL, NULL, + SYSPROF_TYPE_ELF, + (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); +} + +static void +sysprof_elf_init (SysprofElf *self) +{ +} + +SysprofElf * +sysprof_elf_new (const char *filename, + GError **error) +{ + g_return_val_if_fail (filename != NULL, NULL); + + return NULL; +} + +const char * +sysprof_elf_get_build_id (SysprofElf *self) +{ + g_return_val_if_fail (SYSPROF_IS_ELF (self), NULL); + + return self->build_id; +} + +const char * +sysprof_elf_get_debug_link (SysprofElf *self) +{ + g_return_val_if_fail (SYSPROF_IS_ELF (self), NULL); + + return self->debug_link; +} + +const char * +sysprof_elf_get_symbol_at_address (SysprofElf *self, + guint64 address, + guint64 *begin_address, + guint64 *end_address) +{ + g_return_val_if_fail (SYSPROF_IS_ELF (self), NULL); + g_return_val_if_fail (begin_address != NULL, NULL); + g_return_val_if_fail (end_address != NULL, NULL); + + *begin_address = *end_address = 0; + + return NULL; +} + +/** + * sysprof_elf_get_debug_link_elf: + * @self: a #SysprofElf + * + * Gets a #SysprofElf that was resolved from the `.gnu_debuglink` + * ELF section header. + * + * Returns: (transfer none) (nullable): a #SysprofElf or %NULL + */ +SysprofElf * +sysprof_elf_get_debug_link_elf (SysprofElf *self) +{ + g_return_val_if_fail (SYSPROF_IS_ELF (self), NULL); + + return self->debug_link_elf; +} + +void +sysprof_elf_set_debug_link_elf (SysprofElf *self, + SysprofElf *debug_link_elf) +{ + g_return_if_fail (SYSPROF_IS_ELF (self)); + g_return_if_fail (!debug_link_elf || SYSPROF_IS_ELF (debug_link_elf)); + + if (g_set_object (&self->debug_link_elf, debug_link_elf)) + g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_DEBUG_LINK_ELF]); +} From 487e88c8f0c23f6d60339abcadcf63ae9fa0e0d9 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 18 May 2023 13:10:30 -0700 Subject: [PATCH 0149/1030] libsysprof-analyze: add private getter for process info --- src/libsysprof-analyze/sysprof-document-process-private.h | 5 +++-- src/libsysprof-analyze/sysprof-document-process.c | 8 ++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-document-process-private.h b/src/libsysprof-analyze/sysprof-document-process-private.h index 66d7e532..e345e15a 100644 --- a/src/libsysprof-analyze/sysprof-document-process-private.h +++ b/src/libsysprof-analyze/sysprof-document-process-private.h @@ -25,7 +25,8 @@ G_BEGIN_DECLS -void _sysprof_document_process_set_info (SysprofDocumentProcess *self, - SysprofProcessInfo *process_info); +SysprofProcessInfo *_sysprof_document_process_get_info (SysprofDocumentProcess *self); +void _sysprof_document_process_set_info (SysprofDocumentProcess *self, + SysprofProcessInfo *process_info); G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-document-process.c b/src/libsysprof-analyze/sysprof-document-process.c index 48bbca32..3c7d814d 100644 --- a/src/libsysprof-analyze/sysprof-document-process.c +++ b/src/libsysprof-analyze/sysprof-document-process.c @@ -145,6 +145,14 @@ sysprof_document_process_list_mounts (SysprofDocumentProcess *self) return g_object_ref (G_LIST_MODEL (self->process_info->mount_namespace)); } +SysprofProcessInfo * +_sysprof_document_process_get_info (SysprofDocumentProcess *self) +{ + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_PROCESS (self), NULL); + + return self->process_info; +} + void _sysprof_document_process_set_info (SysprofDocumentProcess *self, SysprofProcessInfo *process_info) From da58a52bf3e5f758ccc29025611d44055ced86ef Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 18 May 2023 13:10:51 -0700 Subject: [PATCH 0150/1030] libsysprof-analyze: give access to SysprofElf filename --- src/libsysprof-analyze/sysprof-elf-private.h | 1 + src/libsysprof-analyze/sysprof-elf.c | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-elf-private.h b/src/libsysprof-analyze/sysprof-elf-private.h index eb99edb4..095f039f 100644 --- a/src/libsysprof-analyze/sysprof-elf-private.h +++ b/src/libsysprof-analyze/sysprof-elf-private.h @@ -30,6 +30,7 @@ G_DECLARE_FINAL_TYPE (SysprofElf, sysprof_elf, SYSPROF, ELF, GObject) SysprofElf *sysprof_elf_new (const char *filename, GError **error); +const char *sysprof_elf_get_file (SysprofElf *self); const char *sysprof_elf_get_build_id (SysprofElf *self); const char *sysprof_elf_get_debug_link (SysprofElf *self); const char *sysprof_elf_get_symbol_at_address (SysprofElf *self, diff --git a/src/libsysprof-analyze/sysprof-elf.c b/src/libsysprof-analyze/sysprof-elf.c index c3662479..8b988cda 100644 --- a/src/libsysprof-analyze/sysprof-elf.c +++ b/src/libsysprof-analyze/sysprof-elf.c @@ -27,6 +27,7 @@ struct _SysprofElf GObject parent_instance; char *build_id; char *debug_link; + char *file; SysprofElf *debug_link_elf; }; @@ -35,6 +36,7 @@ enum { PROP_BUILD_ID, PROP_DEBUG_LINK, PROP_DEBUG_LINK_ELF, + PROP_FILE, N_PROPS }; @@ -49,6 +51,7 @@ sysprof_elf_finalize (GObject *object) g_clear_pointer (&self->build_id, g_free); g_clear_pointer (&self->debug_link, g_free); + g_clear_pointer (&self->file, g_free); g_clear_object (&self->debug_link_elf); G_OBJECT_CLASS (sysprof_elf_parent_class)->finalize (object); @@ -76,6 +79,10 @@ sysprof_elf_get_property (GObject *object, g_value_set_object (value, sysprof_elf_get_debug_link_elf (self)); break; + case PROP_FILE: + g_value_set_string (value, sysprof_elf_get_file (self)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } @@ -124,6 +131,11 @@ sysprof_elf_class_init (SysprofElfClass *klass) SYSPROF_TYPE_ELF, (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); + properties [PROP_FILE] = + g_param_spec_string ("file", NULL, NULL, + NULL, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_properties (object_class, N_PROPS, properties); } @@ -141,6 +153,14 @@ sysprof_elf_new (const char *filename, return NULL; } +const char * +sysprof_elf_get_file (SysprofElf *self) +{ + g_return_val_if_fail (SYSPROF_IS_ELF (self), NULL); + + return self->file; +} + const char * sysprof_elf_get_build_id (SysprofElf *self) { From ed19b3d2b6aaddb83319654eb0edfec848290e05 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 18 May 2023 13:11:08 -0700 Subject: [PATCH 0151/1030] libsysprof-analyze: add missing SysprofElfLoader ctor --- src/libsysprof-analyze/sysprof-elf-loader.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-elf-loader.c b/src/libsysprof-analyze/sysprof-elf-loader.c index 2a75b918..f4602435 100644 --- a/src/libsysprof-analyze/sysprof-elf-loader.c +++ b/src/libsysprof-analyze/sysprof-elf-loader.c @@ -43,6 +43,12 @@ G_DEFINE_FINAL_TYPE (SysprofElfLoader, sysprof_elf_loader, G_TYPE_OBJECT) static GParamSpec *properties [N_PROPS]; +SysprofElfLoader * +sysprof_elf_loader_new (void) +{ + return g_object_new (SYSPROF_TYPE_ELF_LOADER, NULL); +} + static void sysprof_elf_loader_finalize (GObject *object) { From 978bfc56801a4d32f92e11835b123af187050c36 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 18 May 2023 13:11:44 -0700 Subject: [PATCH 0152/1030] libsysprof-analyze: add tool to test elf loader --- src/libsysprof-analyze/tests/meson.build | 1 + .../tests/test-elf-loader.c | 84 +++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 src/libsysprof-analyze/tests/test-elf-loader.c diff --git a/src/libsysprof-analyze/tests/meson.build b/src/libsysprof-analyze/tests/meson.build index 1126df96..c57a231a 100644 --- a/src/libsysprof-analyze/tests/meson.build +++ b/src/libsysprof-analyze/tests/meson.build @@ -13,6 +13,7 @@ libsysprof_analyze_testsuite_c_args = [ libsysprof_analyze_testsuite = { 'test-capture-model' : {'skip': true}, + 'test-elf-loader' : {'skip': true}, 'test-list-files' : {'skip': true}, 'test-print-file' : {'skip': true}, 'test-list-processes' : {'skip': true}, diff --git a/src/libsysprof-analyze/tests/test-elf-loader.c b/src/libsysprof-analyze/tests/test-elf-loader.c new file mode 100644 index 00000000..73616e0b --- /dev/null +++ b/src/libsysprof-analyze/tests/test-elf-loader.c @@ -0,0 +1,84 @@ +/* test-elf-loader.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include + +#include "sysprof-document-process-private.h" +#include "sysprof-elf-loader-private.h" + +static const GOptionEntry entries[] = { + { 0 } +}; + +int +main (int argc, + char *argv[]) +{ + g_autoptr(GOptionContext) context = g_option_context_new ("- test loading of ELF files"); + g_autoptr(GMainLoop) main_loop = g_main_loop_new (NULL, FALSE); + g_autoptr(SysprofDocumentLoader) loader = NULL; + g_autoptr(SysprofElfLoader) elf_loader = NULL; + g_autoptr(SysprofDocument) document = NULL; + g_autoptr(GListModel) processes = NULL; + g_autoptr(GError) error = NULL; + guint n_processes; + + g_option_context_add_main_entries (context, entries, NULL); + if (!g_option_context_parse (context, &argc, &argv, &error)) + g_error ("%s", error->message); + + if (argc < 2) + g_error ("usage: %s CAPTURE_FILE", argv[0]); + + loader = sysprof_document_loader_new (argv[1]); + sysprof_document_loader_set_symbolizer (loader, sysprof_no_symbolizer_get ()); + if (!(document = sysprof_document_loader_load (loader, NULL, &error))) + g_error ("Failed to load document: %s", error->message); + + processes = sysprof_document_list_processes (document); + n_processes = g_list_model_get_n_items (processes); + + elf_loader = sysprof_elf_loader_new (); + + for (guint i = 0; i < n_processes; i++) + { + g_autoptr(SysprofDocumentProcess) process = g_list_model_get_item (processes, i); + g_autoptr(GListModel) memory_maps = sysprof_document_process_list_memory_maps (process); + guint n_memory_maps = g_list_model_get_n_items (memory_maps); + SysprofProcessInfo *info = _sysprof_document_process_get_info (process); + + for (guint j = 0; j < n_memory_maps; j++) + { + g_autoptr(SysprofDocumentMmap) memory_map = g_list_model_get_item (memory_maps, j); + const char *file = sysprof_document_mmap_get_file (memory_map); + const char *build_id = sysprof_document_mmap_get_build_id (memory_map); + g_autoptr(SysprofElf) elf = sysprof_elf_loader_load (elf_loader, info->mount_namespace, file, build_id, &error); + + if (elf == NULL) + g_print ("%u: %s [unresolved]\n", info->pid, file); + else + g_print ("%u: %s [%s]\n", info->pid, file, sysprof_elf_get_file (elf)); + + g_clear_error (&error); + } + } + + return 0; +} From 685f9e8baba2a32f8e61edfd2f13eb5179c45f33 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 18 May 2023 15:44:20 -0700 Subject: [PATCH 0153/1030] libsysprof-analyze: add scaffolding for translate API --- .../sysprof-mount-namespace-private.h | 2 -- .../sysprof-mount-namespace.c | 27 +++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-mount-namespace-private.h b/src/libsysprof-analyze/sysprof-mount-namespace-private.h index 28b110d9..4f4ecd26 100644 --- a/src/libsysprof-analyze/sysprof-mount-namespace-private.h +++ b/src/libsysprof-analyze/sysprof-mount-namespace-private.h @@ -39,7 +39,5 @@ void sysprof_mount_namespace_add_mount (SysprofMountNamespa SysprofMount *mount); char **sysprof_mount_namespace_translate (SysprofMountNamespace *self, const char *path); -GMappedFile *sysprof_mount_namespace_open (SysprofMountNamespace *self, - const char *path); G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-mount-namespace.c b/src/libsysprof-analyze/sysprof-mount-namespace.c index c096aebf..8e556d39 100644 --- a/src/libsysprof-analyze/sysprof-mount-namespace.c +++ b/src/libsysprof-analyze/sysprof-mount-namespace.c @@ -146,3 +146,30 @@ sysprof_mount_namespace_add_mount (SysprofMountNamespace *self, g_ptr_array_add (self->mounts, mount); } + +/** + * sysprof_mount_namespace_translate: + * @self: a #SysprofMountNamespace + * @file: the path within the mount namespace to translate + * + * Attempts to translate a path within the mount namespace into a + * path available in our current mount namespace. + * + * As overlays are involved, multiple paths may be returned which + * could contain the target file. You should check these starting + * from the first element in the resulting array to the last. + * + * Returns: (transfer full) (nullable): a UTF-8 encoded string array + * if successful; otherwise %NULL and @error is set. + */ +char ** +sysprof_mount_namespace_translate (SysprofMountNamespace *self, + const char *file) +{ + g_return_val_if_fail (SYSPROF_IS_MOUNT_NAMESPACE (self), NULL); + g_return_val_if_fail (file != NULL, NULL); + + /* TODO: */ + + return NULL; +} From 5c1802d77f571bca06df000dd503cb717a0a479d Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 18 May 2023 15:44:36 -0700 Subject: [PATCH 0154/1030] libsysprof-analyze: add scaffolding for ELF loading --- src/libsysprof-analyze/sysprof-elf-loader.c | 54 ++++++++++++++++++++ src/libsysprof-analyze/sysprof-elf-private.h | 24 ++++----- src/libsysprof-analyze/sysprof-elf.c | 6 +-- 3 files changed, 69 insertions(+), 15 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-elf-loader.c b/src/libsysprof-analyze/sysprof-elf-loader.c index f4602435..1437c827 100644 --- a/src/libsysprof-analyze/sysprof-elf-loader.c +++ b/src/libsysprof-analyze/sysprof-elf-loader.c @@ -20,6 +20,7 @@ #include "config.h" +#include "sysprof-elf-private.h" #include "sysprof-elf-loader-private.h" #include "sysprof-strings-private.h" @@ -228,6 +229,29 @@ sysprof_elf_loader_set_external_debug_dirs (SysprofElfLoader *self, g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_EXTERNAL_DEBUG_DIRS]); } +static void +sysprof_elf_loader_annotate (SysprofElfLoader *self, + SysprofMountNamespace *mount_namespace, + SysprofElf *elf, + const char *debug_link) +{ + g_assert (SYSPROF_IS_ELF_LOADER (self)); + g_assert (SYSPROF_IS_MOUNT_NAMESPACE (mount_namespace)); + g_assert (SYSPROF_IS_ELF (elf)); + g_assert (debug_link != NULL); + + /* TODO: We want to use the debug_link to find a similar file that will + * contain the various debugsymbols. We need to look for it in + * any of the #SysprofElfLoader:debug-dirs (and translated via the + * mount namespace) as well as any of the :external_debug_dirs + * which is a path available to us from our application's mount + * namespace. We recursively follow those debug_link (and must + * protect against cycles) to get the final/best debuglink file. + * That will get assigned via sysprof_elf_set_debug_link_elf(). + */ + +} + /** * sysprof_elf_loader_load: * @self: a #SysprofElfLoader @@ -252,7 +276,37 @@ sysprof_elf_loader_load (SysprofElfLoader *self, const char *build_id, GError **error) { + g_auto(GStrv) paths = NULL; + g_return_val_if_fail (SYSPROF_IS_ELF_LOADER (self), NULL); + g_return_val_if_fail (SYSPROF_IS_MOUNT_NAMESPACE (mount_namespace), NULL); + + /* We must translate the file into a number of paths that may possibly + * locate the file in the case that there are overlays in the mount + * namespace. Each of the paths could be in a lower overlay layer. + */ + if (!(paths = sysprof_mount_namespace_translate (mount_namespace, file))) + goto failure; + + for (guint i = 0; paths[i]; i++) + { + g_autoptr(GMappedFile) mapped_file = NULL; + g_autoptr(SysprofElf) elf = NULL; + g_autoptr(GError) local_error = NULL; + const char *path = paths[i]; + const char *debug_link; + + if (!(mapped_file = g_mapped_file_new (path, FALSE, NULL))) + continue; + + if (!(elf = sysprof_elf_new (mapped_file, &local_error))) + continue; + + if ((debug_link = sysprof_elf_get_debug_link (elf))) + sysprof_elf_loader_annotate (self, mount_namespace, elf, debug_link); + + return g_steal_pointer (&elf); + } failure: g_set_error_literal (error, diff --git a/src/libsysprof-analyze/sysprof-elf-private.h b/src/libsysprof-analyze/sysprof-elf-private.h index 095f039f..b0191389 100644 --- a/src/libsysprof-analyze/sysprof-elf-private.h +++ b/src/libsysprof-analyze/sysprof-elf-private.h @@ -28,17 +28,17 @@ G_BEGIN_DECLS G_DECLARE_FINAL_TYPE (SysprofElf, sysprof_elf, SYSPROF, ELF, GObject) -SysprofElf *sysprof_elf_new (const char *filename, - GError **error); -const char *sysprof_elf_get_file (SysprofElf *self); -const char *sysprof_elf_get_build_id (SysprofElf *self); -const char *sysprof_elf_get_debug_link (SysprofElf *self); -const char *sysprof_elf_get_symbol_at_address (SysprofElf *self, - guint64 address, - guint64 *begin_address, - 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); +SysprofElf *sysprof_elf_new (GMappedFile *mapped_file, + GError **error); +const char *sysprof_elf_get_file (SysprofElf *self); +const char *sysprof_elf_get_build_id (SysprofElf *self); +const char *sysprof_elf_get_debug_link (SysprofElf *self); +const char *sysprof_elf_get_symbol_at_address (SysprofElf *self, + guint64 address, + guint64 *begin_address, + 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); G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-elf.c b/src/libsysprof-analyze/sysprof-elf.c index 8b988cda..e287fd2b 100644 --- a/src/libsysprof-analyze/sysprof-elf.c +++ b/src/libsysprof-analyze/sysprof-elf.c @@ -145,10 +145,10 @@ sysprof_elf_init (SysprofElf *self) } SysprofElf * -sysprof_elf_new (const char *filename, - GError **error) +sysprof_elf_new (GMappedFile *mapped_file, + GError **error) { - g_return_val_if_fail (filename != NULL, NULL); + g_return_val_if_fail (mapped_file != NULL, NULL); return NULL; } From d448519c29ba801d0648a81759c76b930e8523e0 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 18 May 2023 16:23:12 -0700 Subject: [PATCH 0155/1030] libsysprof-analyze: prefix private API with _ --- src/libsysprof-analyze/sysprof-document.c | 2 +- src/libsysprof-analyze/sysprof-mount-private.h | 4 ++-- src/libsysprof-analyze/sysprof-mount.c | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index f5f2c8a3..40713399 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -362,7 +362,7 @@ sysprof_document_load_mountinfo (SysprofDocument *self, line[line_len] = 0; - if ((mount = sysprof_mount_new_for_mountinfo (self->strings, line))) + if ((mount = _sysprof_mount_new_for_mountinfo (self->strings, line))) sysprof_mount_namespace_add_mount (process_info->mount_namespace, g_steal_pointer (&mount)); } } diff --git a/src/libsysprof-analyze/sysprof-mount-private.h b/src/libsysprof-analyze/sysprof-mount-private.h index 37bf86cb..9662bccc 100644 --- a/src/libsysprof-analyze/sysprof-mount-private.h +++ b/src/libsysprof-analyze/sysprof-mount-private.h @@ -25,7 +25,7 @@ G_BEGIN_DECLS -SysprofMount *sysprof_mount_new_for_mountinfo (SysprofStrings *strings, - const char *mountinfo); +SysprofMount *_sysprof_mount_new_for_mountinfo (SysprofStrings *strings, + const char *mountinfo); G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-mount.c b/src/libsysprof-analyze/sysprof-mount.c index b5b4f2f7..09f5801a 100644 --- a/src/libsysprof-analyze/sysprof-mount.c +++ b/src/libsysprof-analyze/sysprof-mount.c @@ -183,8 +183,8 @@ sysprof_mount_init (SysprofMount *self) } SysprofMount * -sysprof_mount_new_for_mountinfo (SysprofStrings *strings, - const char *mountinfo) +_sysprof_mount_new_for_mountinfo (SysprofStrings *strings, + const char *mountinfo) { g_autoptr(SysprofMount) self = NULL; g_auto(GStrv) parts = NULL; From 7db4540f9a8d6c8fb9ffafb87e6bd30712a0c5b1 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 19 May 2023 10:01:02 -0700 Subject: [PATCH 0156/1030] libsysprof-analyze: rename mount-path to mount-point We want to be consistent with SysprofMount in naming. --- src/libsysprof-analyze/sysprof-document.c | 2 +- .../sysprof-mount-device-private.h | 20 ++++++------- src/libsysprof-analyze/sysprof-mount-device.c | 28 +++++++++---------- .../sysprof-mount-private.h | 2 ++ 4 files changed, 27 insertions(+), 25 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index 40713399..fd3b28a5 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -324,7 +324,7 @@ sysprof_document_load_mounts (SysprofDocument *self) mount_device = sysprof_mount_device_new (); sysprof_mount_device_set_id (mount_device, device); - sysprof_mount_device_set_mount_path (mount_device, mountpoint); + sysprof_mount_device_set_mount_point (mount_device, mountpoint); sysprof_mount_device_set_subvolume (mount_device, subvol); sysprof_mount_namespace_add_device (self->mount_namespace, g_steal_pointer (&mount_device)); } diff --git a/src/libsysprof-analyze/sysprof-mount-device-private.h b/src/libsysprof-analyze/sysprof-mount-device-private.h index cfebc3bd..db9e4f5a 100644 --- a/src/libsysprof-analyze/sysprof-mount-device-private.h +++ b/src/libsysprof-analyze/sysprof-mount-device-private.h @@ -28,15 +28,15 @@ G_BEGIN_DECLS G_DECLARE_FINAL_TYPE (SysprofMountDevice, sysprof_mount_device, SYSPROF, MOUNT_DEVICE, GObject) -SysprofMountDevice *sysprof_mount_device_new (void); -const char *sysprof_mount_device_get_id (SysprofMountDevice *self); -void sysprof_mount_device_set_id (SysprofMountDevice *self, - const char *id); -const char *sysprof_mount_device_get_mount_path (SysprofMountDevice *self); -void sysprof_mount_device_set_mount_path (SysprofMountDevice *self, - const char *mount_path); -const char *sysprof_mount_device_get_subvolume (SysprofMountDevice *self); -void sysprof_mount_device_set_subvolume (SysprofMountDevice *self, - const char *subvolume); +SysprofMountDevice *sysprof_mount_device_new (void); +const char *sysprof_mount_device_get_id (SysprofMountDevice *self); +void sysprof_mount_device_set_id (SysprofMountDevice *self, + const char *id); +const char *sysprof_mount_device_get_mount_point (SysprofMountDevice *self); +void sysprof_mount_device_set_mount_point (SysprofMountDevice *self, + const char *mount_point); +const char *sysprof_mount_device_get_subvolume (SysprofMountDevice *self); +void sysprof_mount_device_set_subvolume (SysprofMountDevice *self, + const char *subvolume); G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-mount-device.c b/src/libsysprof-analyze/sysprof-mount-device.c index 3b61fc3b..983e8907 100644 --- a/src/libsysprof-analyze/sysprof-mount-device.c +++ b/src/libsysprof-analyze/sysprof-mount-device.c @@ -26,14 +26,14 @@ struct _SysprofMountDevice { GObject parent_instance; char *id; - char *mount_path; + char *mount_point; char *subvolume; }; enum { PROP_0, PROP_ID, - PROP_MOUNT_PATH, + PROP_MOUNT_POINT, PROP_SUBVOLUME, N_PROPS }; @@ -48,7 +48,7 @@ sysprof_mount_device_finalize (GObject *object) SysprofMountDevice *self = (SysprofMountDevice *)object; g_clear_pointer (&self->id, g_free); - g_clear_pointer (&self->mount_path, g_free); + g_clear_pointer (&self->mount_point, g_free); g_clear_pointer (&self->subvolume, g_free); G_OBJECT_CLASS (sysprof_mount_device_parent_class)->finalize (object); @@ -68,8 +68,8 @@ sysprof_mount_device_get_property (GObject *object, g_value_set_string (value, sysprof_mount_device_get_id (self)); break; - case PROP_MOUNT_PATH: - g_value_set_string (value, sysprof_mount_device_get_mount_path (self)); + case PROP_MOUNT_POINT: + g_value_set_string (value, sysprof_mount_device_get_mount_point (self)); break; case PROP_SUBVOLUME: @@ -95,8 +95,8 @@ sysprof_mount_device_set_property (GObject *object, sysprof_mount_device_set_id (self, g_value_get_string (value)); break; - case PROP_MOUNT_PATH: - sysprof_mount_device_set_mount_path (self, g_value_get_string (value)); + case PROP_MOUNT_POINT: + sysprof_mount_device_set_mount_point (self, g_value_get_string (value)); break; case PROP_SUBVOLUME: @@ -122,7 +122,7 @@ sysprof_mount_device_class_init (SysprofMountDeviceClass *klass) NULL, (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); - properties [PROP_MOUNT_PATH] = + properties [PROP_MOUNT_POINT] = g_param_spec_string ("mount-path", NULL, NULL, NULL, (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); @@ -165,21 +165,21 @@ sysprof_mount_device_set_id (SysprofMountDevice *self, } const char * -sysprof_mount_device_get_mount_path (SysprofMountDevice *self) +sysprof_mount_device_get_mount_point (SysprofMountDevice *self) { g_return_val_if_fail (SYSPROF_IS_MOUNT_DEVICE (self), NULL); - return self->mount_path; + return self->mount_point; } void -sysprof_mount_device_set_mount_path (SysprofMountDevice *self, - const char *mount_path) +sysprof_mount_device_set_mount_point (SysprofMountDevice *self, + const char *mount_point) { g_return_if_fail (SYSPROF_IS_MOUNT_DEVICE (self)); - if (g_set_str (&self->mount_path, mount_path)) - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_MOUNT_PATH]); + if (g_set_str (&self->mount_point, mount_point)) + g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_MOUNT_POINT]); } const char * diff --git a/src/libsysprof-analyze/sysprof-mount-private.h b/src/libsysprof-analyze/sysprof-mount-private.h index 9662bccc..11488d05 100644 --- a/src/libsysprof-analyze/sysprof-mount-private.h +++ b/src/libsysprof-analyze/sysprof-mount-private.h @@ -27,5 +27,7 @@ G_BEGIN_DECLS SysprofMount *_sysprof_mount_new_for_mountinfo (SysprofStrings *strings, const char *mountinfo); +gboolean _sysprof_mount_contains_path (SysprofMount *self, + const char *path); G_END_DECLS From 63168656a5043fadf3909c12b6145a5dfb13a4bb Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 19 May 2023 10:02:20 -0700 Subject: [PATCH 0157/1030] libsysprof-analyze: add get_relative_path helper This acts somewhat like g_file_get_relative_path() in that if it is not a subdirectory of the parent, NULL is returned. Otherwise the relative path is returned. We can just dive into the substring instead of copying which is a bonus point. --- .../sysprof-mount-private.h | 2 +- src/libsysprof-analyze/sysprof-mount.c | 30 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/libsysprof-analyze/sysprof-mount-private.h b/src/libsysprof-analyze/sysprof-mount-private.h index 11488d05..fe965c2c 100644 --- a/src/libsysprof-analyze/sysprof-mount-private.h +++ b/src/libsysprof-analyze/sysprof-mount-private.h @@ -27,7 +27,7 @@ G_BEGIN_DECLS SysprofMount *_sysprof_mount_new_for_mountinfo (SysprofStrings *strings, const char *mountinfo); -gboolean _sysprof_mount_contains_path (SysprofMount *self, +const char *_sysprof_mount_get_relative_path (SysprofMount *self, const char *path); G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-mount.c b/src/libsysprof-analyze/sysprof-mount.c index 09f5801a..76b18b65 100644 --- a/src/libsysprof-analyze/sysprof-mount.c +++ b/src/libsysprof-analyze/sysprof-mount.c @@ -331,3 +331,33 @@ sysprof_mount_get_parent_mount_id (SysprofMount *self) { return self->parent_mount_id; } + +static inline gboolean +mount_is_root (SysprofMount *self) +{ + return self->mount_point[0] == '/' && self->mount_point[1] == 0; +} + +const char * +_sysprof_mount_get_relative_path (SysprofMount *self, + const char *path) +{ + gsize len; + + g_return_val_if_fail (SYSPROF_IS_MOUNT (self), NULL); + + if (path == NULL || self->mount_point == NULL) + return NULL; + + len = g_ref_string_length (self->mount_point); + + /* We don't care about directory paths, so ensure that we both + * have the proper prefix and that it is in a subdirectory of this + * mount point. + */ + if (!mount_is_root (self) && + (!g_str_has_prefix (path, self->mount_point) || path[len] != '/')) + return NULL; + + return &path[len]; +} From aaa12307f21cda607f7e2686d103b3cfa0035aa0 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 19 May 2023 10:06:51 -0700 Subject: [PATCH 0158/1030] libsysprof-analyze: fix name of property --- src/libsysprof-analyze/sysprof-mount-device.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libsysprof-analyze/sysprof-mount-device.c b/src/libsysprof-analyze/sysprof-mount-device.c index 983e8907..1d08dd44 100644 --- a/src/libsysprof-analyze/sysprof-mount-device.c +++ b/src/libsysprof-analyze/sysprof-mount-device.c @@ -123,7 +123,7 @@ sysprof_mount_device_class_init (SysprofMountDeviceClass *klass) (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); properties [PROP_MOUNT_POINT] = - g_param_spec_string ("mount-path", NULL, NULL, + g_param_spec_string ("mount-point", NULL, NULL, NULL, (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); From e1da2d4f706f998864c479d8998b81ca3f67f6fa Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 19 May 2023 10:11:48 -0700 Subject: [PATCH 0159/1030] libsysprof-analyze: use GRefString for SysprofMountDevice We will end up creating a lot of these, and it's nice to at least be able to share the strings even if we can't always share the objects themselves. --- src/libsysprof-analyze/sysprof-document.c | 12 ++- .../sysprof-mount-device-private.h | 10 +-- src/libsysprof-analyze/sysprof-mount-device.c | 89 +++++-------------- 3 files changed, 28 insertions(+), 83 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index fd3b28a5..d07ebd7a 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -279,9 +279,8 @@ sysprof_document_load_mounts (SysprofDocument *self) line_reader_init (&reader, (char *)contents, contents_len); while ((line = line_reader_next (&reader, &line_len))) { - g_autoptr(SysprofMountDevice) mount_device = NULL; - g_auto(GStrv) parts = NULL; g_autofree char *subvol = NULL; + g_auto(GStrv) parts = NULL; const char *filesystem; const char *mountpoint; const char *device; @@ -322,11 +321,10 @@ sysprof_document_load_mounts (SysprofDocument *self) } } - mount_device = sysprof_mount_device_new (); - sysprof_mount_device_set_id (mount_device, device); - sysprof_mount_device_set_mount_point (mount_device, mountpoint); - sysprof_mount_device_set_subvolume (mount_device, subvol); - sysprof_mount_namespace_add_device (self->mount_namespace, g_steal_pointer (&mount_device)); + sysprof_mount_namespace_add_device (self->mount_namespace, + sysprof_mount_device_new (sysprof_strings_get (self->strings, device), + sysprof_strings_get (self->strings, mountpoint), + sysprof_strings_get (self->strings, subvol))); } } diff --git a/src/libsysprof-analyze/sysprof-mount-device-private.h b/src/libsysprof-analyze/sysprof-mount-device-private.h index db9e4f5a..12b882c9 100644 --- a/src/libsysprof-analyze/sysprof-mount-device-private.h +++ b/src/libsysprof-analyze/sysprof-mount-device-private.h @@ -28,15 +28,11 @@ G_BEGIN_DECLS G_DECLARE_FINAL_TYPE (SysprofMountDevice, sysprof_mount_device, SYSPROF, MOUNT_DEVICE, GObject) -SysprofMountDevice *sysprof_mount_device_new (void); +SysprofMountDevice *sysprof_mount_device_new (GRefString *id, + GRefString *mount_point, + GRefString *subvolume); const char *sysprof_mount_device_get_id (SysprofMountDevice *self); -void sysprof_mount_device_set_id (SysprofMountDevice *self, - const char *id); const char *sysprof_mount_device_get_mount_point (SysprofMountDevice *self); -void sysprof_mount_device_set_mount_point (SysprofMountDevice *self, - const char *mount_point); const char *sysprof_mount_device_get_subvolume (SysprofMountDevice *self); -void sysprof_mount_device_set_subvolume (SysprofMountDevice *self, - const char *subvolume); G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-mount-device.c b/src/libsysprof-analyze/sysprof-mount-device.c index 1d08dd44..59062bc2 100644 --- a/src/libsysprof-analyze/sysprof-mount-device.c +++ b/src/libsysprof-analyze/sysprof-mount-device.c @@ -25,9 +25,9 @@ struct _SysprofMountDevice { GObject parent_instance; - char *id; - char *mount_point; - char *subvolume; + GRefString *id; + GRefString *mount_point; + GRefString *subvolume; }; enum { @@ -47,9 +47,9 @@ sysprof_mount_device_finalize (GObject *object) { SysprofMountDevice *self = (SysprofMountDevice *)object; - g_clear_pointer (&self->id, g_free); - g_clear_pointer (&self->mount_point, g_free); - g_clear_pointer (&self->subvolume, g_free); + g_clear_pointer (&self->id, g_ref_string_release); + g_clear_pointer (&self->mount_point, g_ref_string_release); + g_clear_pointer (&self->subvolume, g_ref_string_release); G_OBJECT_CLASS (sysprof_mount_device_parent_class)->finalize (object); } @@ -81,33 +81,6 @@ sysprof_mount_device_get_property (GObject *object, } } -static void -sysprof_mount_device_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - SysprofMountDevice *self = SYSPROF_MOUNT_DEVICE (object); - - switch (prop_id) - { - case PROP_ID: - sysprof_mount_device_set_id (self, g_value_get_string (value)); - break; - - case PROP_MOUNT_POINT: - sysprof_mount_device_set_mount_point (self, g_value_get_string (value)); - break; - - case PROP_SUBVOLUME: - sysprof_mount_device_set_subvolume (self, g_value_get_string (value)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - static void sysprof_mount_device_class_init (SysprofMountDeviceClass *klass) { @@ -115,22 +88,21 @@ sysprof_mount_device_class_init (SysprofMountDeviceClass *klass) object_class->finalize = sysprof_mount_device_finalize; object_class->get_property = sysprof_mount_device_get_property; - object_class->set_property = sysprof_mount_device_set_property; properties [PROP_ID] = g_param_spec_string ("id", NULL, NULL, NULL, - (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); properties [PROP_MOUNT_POINT] = g_param_spec_string ("mount-point", NULL, NULL, NULL, - (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); properties [PROP_SUBVOLUME] = g_param_spec_string ("subvolume", NULL, NULL, NULL, - (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_object_class_install_properties (object_class, N_PROPS, properties); } @@ -141,9 +113,18 @@ sysprof_mount_device_init (SysprofMountDevice *self) } SysprofMountDevice * -sysprof_mount_device_new (void) +sysprof_mount_device_new (GRefString *id, + GRefString *mount_point, + GRefString *subvolume) { - return g_object_new (SYSPROF_TYPE_MOUNT_DEVICE, NULL); + SysprofMountDevice *self; + + self = g_object_new (SYSPROF_TYPE_MOUNT_DEVICE, NULL); + self->id = id; + self->mount_point = mount_point; + self->subvolume = subvolume; + + return g_steal_pointer (&self); } const char * @@ -154,16 +135,6 @@ sysprof_mount_device_get_id (SysprofMountDevice *self) return self->id; } -void -sysprof_mount_device_set_id (SysprofMountDevice *self, - const char *id) -{ - g_return_if_fail (SYSPROF_IS_MOUNT_DEVICE (self)); - - if (g_set_str (&self->id, id)) - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ID]); -} - const char * sysprof_mount_device_get_mount_point (SysprofMountDevice *self) { @@ -172,16 +143,6 @@ sysprof_mount_device_get_mount_point (SysprofMountDevice *self) return self->mount_point; } -void -sysprof_mount_device_set_mount_point (SysprofMountDevice *self, - const char *mount_point) -{ - g_return_if_fail (SYSPROF_IS_MOUNT_DEVICE (self)); - - if (g_set_str (&self->mount_point, mount_point)) - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_MOUNT_POINT]); -} - const char * sysprof_mount_device_get_subvolume (SysprofMountDevice *self) { @@ -189,13 +150,3 @@ sysprof_mount_device_get_subvolume (SysprofMountDevice *self) return self->subvolume; } - -void -sysprof_mount_device_set_subvolume (SysprofMountDevice *self, - const char *subvolume) -{ - g_return_if_fail (SYSPROF_IS_MOUNT_DEVICE (self)); - - if (g_set_str (&self->subvolume, subvolume)) - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_SUBVOLUME]); -} From 9e2ddae05c6aad66d08f0cbb66c4f8f9ef25cf4e Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 19 May 2023 10:13:01 -0700 Subject: [PATCH 0160/1030] libsysprof-analyze: make string functions recognize NULL --- src/libsysprof-analyze/sysprof-strings.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-strings.c b/src/libsysprof-analyze/sysprof-strings.c index 319d4219..a7c13f4e 100644 --- a/src/libsysprof-analyze/sysprof-strings.c +++ b/src/libsysprof-analyze/sysprof-strings.c @@ -70,6 +70,9 @@ sysprof_strings_get (SysprofStrings *self, { GRefString *ret; + if (string == NULL) + return NULL; + g_mutex_lock (&self->mutex); if (!(ret = g_hash_table_lookup (self->hashtable, string))) { @@ -88,6 +91,9 @@ sysprof_strings_lookup (SysprofStrings *self, { GRefString *ret; + if (string == NULL) + return NULL; + g_mutex_lock (&self->mutex); ret = g_hash_table_lookup (self->hashtable, string); g_mutex_unlock (&self->mutex); From 90519fe830f15ed935ffc0313f00897d132570fc Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 19 May 2023 10:17:03 -0700 Subject: [PATCH 0161/1030] libsysprof-analyze: rename :id to :fs-spec to match fstab This is the description of the field in fstab(5) so we want to match that so it's a bit more clear what we're keying off of when translating to a mount device we can access on the host. --- .../sysprof-mount-device-private.h | 4 ++-- src/libsysprof-analyze/sysprof-mount-device.c | 22 +++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-mount-device-private.h b/src/libsysprof-analyze/sysprof-mount-device-private.h index 12b882c9..dc01b5d6 100644 --- a/src/libsysprof-analyze/sysprof-mount-device-private.h +++ b/src/libsysprof-analyze/sysprof-mount-device-private.h @@ -28,10 +28,10 @@ G_BEGIN_DECLS G_DECLARE_FINAL_TYPE (SysprofMountDevice, sysprof_mount_device, SYSPROF, MOUNT_DEVICE, GObject) -SysprofMountDevice *sysprof_mount_device_new (GRefString *id, +SysprofMountDevice *sysprof_mount_device_new (GRefString *fs_spec, GRefString *mount_point, GRefString *subvolume); -const char *sysprof_mount_device_get_id (SysprofMountDevice *self); +const char *sysprof_mount_device_get_fs_spec (SysprofMountDevice *self); const char *sysprof_mount_device_get_mount_point (SysprofMountDevice *self); const char *sysprof_mount_device_get_subvolume (SysprofMountDevice *self); diff --git a/src/libsysprof-analyze/sysprof-mount-device.c b/src/libsysprof-analyze/sysprof-mount-device.c index 59062bc2..9dd830a7 100644 --- a/src/libsysprof-analyze/sysprof-mount-device.c +++ b/src/libsysprof-analyze/sysprof-mount-device.c @@ -25,14 +25,14 @@ struct _SysprofMountDevice { GObject parent_instance; - GRefString *id; + GRefString *fs_spec; GRefString *mount_point; GRefString *subvolume; }; enum { PROP_0, - PROP_ID, + PROP_FS_SPEC, PROP_MOUNT_POINT, PROP_SUBVOLUME, N_PROPS @@ -47,7 +47,7 @@ sysprof_mount_device_finalize (GObject *object) { SysprofMountDevice *self = (SysprofMountDevice *)object; - g_clear_pointer (&self->id, g_ref_string_release); + g_clear_pointer (&self->fs_spec, g_ref_string_release); g_clear_pointer (&self->mount_point, g_ref_string_release); g_clear_pointer (&self->subvolume, g_ref_string_release); @@ -64,8 +64,8 @@ sysprof_mount_device_get_property (GObject *object, switch (prop_id) { - case PROP_ID: - g_value_set_string (value, sysprof_mount_device_get_id (self)); + case PROP_FS_SPEC: + g_value_set_string (value, sysprof_mount_device_get_fs_spec (self)); break; case PROP_MOUNT_POINT: @@ -89,8 +89,8 @@ sysprof_mount_device_class_init (SysprofMountDeviceClass *klass) object_class->finalize = sysprof_mount_device_finalize; object_class->get_property = sysprof_mount_device_get_property; - properties [PROP_ID] = - g_param_spec_string ("id", NULL, NULL, + properties [PROP_FS_SPEC] = + g_param_spec_string ("fs-spec", NULL, NULL, NULL, (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); @@ -113,14 +113,14 @@ sysprof_mount_device_init (SysprofMountDevice *self) } SysprofMountDevice * -sysprof_mount_device_new (GRefString *id, +sysprof_mount_device_new (GRefString *fs_spec, GRefString *mount_point, GRefString *subvolume) { SysprofMountDevice *self; self = g_object_new (SYSPROF_TYPE_MOUNT_DEVICE, NULL); - self->id = id; + self->fs_spec = fs_spec; self->mount_point = mount_point; self->subvolume = subvolume; @@ -128,11 +128,11 @@ sysprof_mount_device_new (GRefString *id, } const char * -sysprof_mount_device_get_id (SysprofMountDevice *self) +sysprof_mount_device_get_fs_spec (SysprofMountDevice *self) { g_return_val_if_fail (SYSPROF_IS_MOUNT_DEVICE (self), NULL); - return self->id; + return self->fs_spec; } const char * From 1a9e11baef1ce188c186eb4baeafa869bcc64c2d Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 19 May 2023 10:23:48 -0700 Subject: [PATCH 0162/1030] libsysprof-analyze: start on namespace translation --- .../sysprof-mount-namespace.c | 70 ++++++++++++++++++- 1 file changed, 68 insertions(+), 2 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-mount-namespace.c b/src/libsysprof-analyze/sysprof-mount-namespace.c index 8e556d39..f98906cf 100644 --- a/src/libsysprof-analyze/sysprof-mount-namespace.c +++ b/src/libsysprof-analyze/sysprof-mount-namespace.c @@ -147,6 +147,42 @@ sysprof_mount_namespace_add_mount (SysprofMountNamespace *self, g_ptr_array_add (self->mounts, mount); } +static SysprofMountDevice * +sysprof_mount_namespace_find_device (SysprofMountNamespace *self, + SysprofMount *mount, + const char *relative_path) +{ + const char *mount_source; + const char *subvolume; + + g_assert (SYSPROF_IS_MOUNT_NAMESPACE (self)); + g_assert (SYSPROF_IS_MOUNT (mount)); + + mount_source = sysprof_mount_get_mount_source (mount); + subvolume = sysprof_mount_get_superblock_option (mount, "subvol"); + + for (guint i = 0; i < self->devices->len; i++) + { + SysprofMountDevice *device = g_ptr_array_index (self->devices, i); + const char *fs_spec = sysprof_mount_device_get_fs_spec (device); + + if (g_strcmp0 (fs_spec, mount_source) != 0) + continue; + + if (subvolume != NULL) + { + const char *device_subvolume = sysprof_mount_device_get_subvolume (device); + + if (g_strcmp0 (subvolume, device_subvolume) != 0) + continue; + } + + return device; + } + + return NULL; +} + /** * sysprof_mount_namespace_translate: * @self: a #SysprofMountNamespace @@ -166,10 +202,40 @@ char ** sysprof_mount_namespace_translate (SysprofMountNamespace *self, const char *file) { + g_autoptr(GArray) strv = NULL; + g_return_val_if_fail (SYSPROF_IS_MOUNT_NAMESPACE (self), NULL); g_return_val_if_fail (file != NULL, NULL); - /* TODO: */ + strv = g_array_new (TRUE, FALSE, sizeof (char *)); - return NULL; + for (guint i = 0; i < self->mounts->len; i++) + { + SysprofMount *mount = g_ptr_array_index (self->mounts, i); + SysprofMountDevice *device; + const char *device_mount_point; + const char *relative; + char *translated; + + if (!(relative = _sysprof_mount_get_relative_path (mount, file)) || + !(device = sysprof_mount_namespace_find_device (self, mount, relative))) + continue; + + device_mount_point = sysprof_mount_device_get_mount_point (device); + translated = g_build_filename (device_mount_point, relative, NULL); + + /* TODO: Still a bit to do here, because we need to handle overlays + * still as a SysprofMount. Additionally, we may need to adjust the + * paths a bit more based on subvolume, but I need a system such + * as Silverblue or GNOME OS to test that again to match or improve + * on existing behavior in libsysprof. + */ + + g_array_append_val (strv, translated); + } + + if (strv->len == 0) + return NULL; + + return (char **)g_array_free (g_steal_pointer (&strv), FALSE); } From 322a6253ba902a2a5e4845e4f5fcf6581c8d28fb Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 19 May 2023 10:25:13 -0700 Subject: [PATCH 0163/1030] libsysprof-analyze: add note about container breakouts --- src/libsysprof-analyze/sysprof-mount-namespace.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-mount-namespace.c b/src/libsysprof-analyze/sysprof-mount-namespace.c index f98906cf..4fc2cc50 100644 --- a/src/libsysprof-analyze/sysprof-mount-namespace.c +++ b/src/libsysprof-analyze/sysprof-mount-namespace.c @@ -231,6 +231,12 @@ sysprof_mount_namespace_translate (SysprofMountNamespace *self, * on existing behavior in libsysprof. */ + /* TODO: After we've translated to what we'd expect to see on the + * host system, we'll need to translate once again to what we can + * actually access if we're inside a container ourselves, such as + * Toolbox or Flatpak and use /var/run/host or similar. + */ + g_array_append_val (strv, translated); } From 8e101624bc19971aa859155abfb616f2a8b169fa Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 19 May 2023 10:35:31 -0700 Subject: [PATCH 0164/1030] libsysprof: add mmap variant for elfparser --- src/libsysprof/elfparser.c | 31 +++++++++++++++++++++++++++++++ src/libsysprof/elfparser.h | 2 ++ 2 files changed, 33 insertions(+) diff --git a/src/libsysprof/elfparser.c b/src/libsysprof/elfparser.c index 8816a716..2c6b638b 100644 --- a/src/libsysprof/elfparser.c +++ b/src/libsysprof/elfparser.c @@ -297,6 +297,37 @@ open_mapped_file (const char *filename, return file; } +ElfParser * +elf_parser_new_from_mmap (GMappedFile *file, + GError **error) +{ + const guchar *data; + gsize length; + ElfParser *parser; + + if (file == NULL) + return NULL; + + data = (guchar *)g_mapped_file_get_contents (file); + length = g_mapped_file_get_length (file); + parser = elf_parser_new_from_data (data, length); + + if (!parser) + { + g_set_error (error, + G_FILE_ERROR, + G_FILE_ERROR_FAILED, + "Failed to load ELF from mmap region"); + g_mapped_file_unref (file); + return NULL; + } + + parser->filename = NULL; + parser->file = file; + + return parser; +} + ElfParser * elf_parser_new (const char *filename, GError **error) diff --git a/src/libsysprof/elfparser.h b/src/libsysprof/elfparser.h index 94fa5c02..fd19e78d 100644 --- a/src/libsysprof/elfparser.h +++ b/src/libsysprof/elfparser.h @@ -27,6 +27,8 @@ typedef struct ElfParser ElfParser; ElfParser *elf_parser_new_from_data (const guchar *data, gsize length); +ElfParser *elf_parser_new_from_mmap (GMappedFile *mapped_file, + GError **err); ElfParser *elf_parser_new (const char *filename, GError **err); void elf_parser_free (ElfParser *parser); From aa829f8665f3dc1919901f80d53290c500a1c290 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 19 May 2023 10:40:50 -0700 Subject: [PATCH 0165/1030] libsysprof-analyze: load ElfParser for GMappedFile --- src/libsysprof-analyze/sysprof-elf-loader.c | 2 +- src/libsysprof-analyze/sysprof-elf-private.h | 3 ++- src/libsysprof-analyze/sysprof-elf.c | 19 +++++++++++++++++-- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-elf-loader.c b/src/libsysprof-analyze/sysprof-elf-loader.c index 1437c827..c035f797 100644 --- a/src/libsysprof-analyze/sysprof-elf-loader.c +++ b/src/libsysprof-analyze/sysprof-elf-loader.c @@ -299,7 +299,7 @@ sysprof_elf_loader_load (SysprofElfLoader *self, if (!(mapped_file = g_mapped_file_new (path, FALSE, NULL))) continue; - if (!(elf = sysprof_elf_new (mapped_file, &local_error))) + if (!(elf = sysprof_elf_new (path, g_steal_pointer (&mapped_file), &local_error))) continue; if ((debug_link = sysprof_elf_get_debug_link (elf))) diff --git a/src/libsysprof-analyze/sysprof-elf-private.h b/src/libsysprof-analyze/sysprof-elf-private.h index b0191389..95a1d51b 100644 --- a/src/libsysprof-analyze/sysprof-elf-private.h +++ b/src/libsysprof-analyze/sysprof-elf-private.h @@ -28,7 +28,8 @@ G_BEGIN_DECLS G_DECLARE_FINAL_TYPE (SysprofElf, sysprof_elf, SYSPROF, ELF, GObject) -SysprofElf *sysprof_elf_new (GMappedFile *mapped_file, +SysprofElf *sysprof_elf_new (const char *filename, + GMappedFile *mapped_file, GError **error); const char *sysprof_elf_get_file (SysprofElf *self); const char *sysprof_elf_get_build_id (SysprofElf *self); diff --git a/src/libsysprof-analyze/sysprof-elf.c b/src/libsysprof-analyze/sysprof-elf.c index e287fd2b..1cc50502 100644 --- a/src/libsysprof-analyze/sysprof-elf.c +++ b/src/libsysprof-analyze/sysprof-elf.c @@ -20,6 +20,8 @@ #include "config.h" +#include "../libsysprof/elfparser.h" + #include "sysprof-elf-private.h" struct _SysprofElf @@ -29,6 +31,7 @@ struct _SysprofElf char *debug_link; char *file; SysprofElf *debug_link_elf; + ElfParser *parser; }; enum { @@ -52,6 +55,7 @@ sysprof_elf_finalize (GObject *object) g_clear_pointer (&self->build_id, g_free); g_clear_pointer (&self->debug_link, g_free); g_clear_pointer (&self->file, g_free); + g_clear_pointer (&self->parser, elf_parser_free); g_clear_object (&self->debug_link_elf); G_OBJECT_CLASS (sysprof_elf_parent_class)->finalize (object); @@ -145,12 +149,23 @@ sysprof_elf_init (SysprofElf *self) } SysprofElf * -sysprof_elf_new (GMappedFile *mapped_file, +sysprof_elf_new (const char *filename, + GMappedFile *mapped_file, GError **error) { + SysprofElf *self; + ElfParser *parser; + g_return_val_if_fail (mapped_file != NULL, NULL); - return NULL; + if (!(parser = elf_parser_new_from_mmap (g_steal_pointer (&mapped_file), error))) + return NULL; + + self = g_object_new (SYSPROF_TYPE_ELF, NULL); + self->file = g_strdup (filename); + self->parser = g_steal_pointer (&parser); + + return self; } const char * From cb74b88b1b8714e3660df0bef94a38ed14340f27 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 19 May 2023 13:33:41 -0700 Subject: [PATCH 0166/1030] libsysprof-analyze: try /var/run/host in flatpak/podman In podman (at least with toolbox) it appears to give access to home via /var/run/host/home/ so we don't need to translate home paths at all. However, Flatpak does not give you home access via that path so we just have to hope that we have access to $HOME from whatever application mount namespace we're running from. That means to do symbolizing in a Flatpak app you'd likely need --filesystem=host to be useful. --- src/libsysprof-analyze/sysprof-elf-loader.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-elf-loader.c b/src/libsysprof-analyze/sysprof-elf-loader.c index c035f797..487c2f3f 100644 --- a/src/libsysprof-analyze/sysprof-elf-loader.c +++ b/src/libsysprof-analyze/sysprof-elf-loader.c @@ -43,6 +43,8 @@ enum { G_DEFINE_FINAL_TYPE (SysprofElfLoader, sysprof_elf_loader, G_TYPE_OBJECT) static GParamSpec *properties [N_PROPS]; +static gboolean in_flatpak; +static gboolean in_podman; SysprofElfLoader * sysprof_elf_loader_new (void) @@ -127,6 +129,9 @@ sysprof_elf_loader_class_init (SysprofElfLoaderClass *klass) (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); g_object_class_install_properties (object_class, N_PROPS, properties); + + in_flatpak = g_file_test ("/.flatpak-info", G_FILE_TEST_EXISTS); + in_podman = g_file_test ("/run/.containerenv", G_FILE_TEST_EXISTS); } static void @@ -293,9 +298,13 @@ sysprof_elf_loader_load (SysprofElfLoader *self, g_autoptr(GMappedFile) mapped_file = NULL; g_autoptr(SysprofElf) elf = NULL; g_autoptr(GError) local_error = NULL; + g_autofree char *container_path = NULL; const char *path = paths[i]; const char *debug_link; + if ((in_flatpak && !g_str_has_prefix (path, "/home/")) || in_podman) + path = container_path = g_build_filename ("/var/run/host", path, NULL); + if (!(mapped_file = g_mapped_file_new (path, FALSE, NULL))) continue; From 269a8ea65815ca34013bbe46e728bfe2706edf8b Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 19 May 2023 13:50:46 -0700 Subject: [PATCH 0167/1030] libsysprof-analyze: insert and lookup ELF from cache We do need to at least translate the path to what we would want to see from the host system before inserting/resolving, so that we don't risk collisions in the cache. --- src/libsysprof-analyze/sysprof-elf-loader.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-elf-loader.c b/src/libsysprof-analyze/sysprof-elf-loader.c index 487c2f3f..e466d8b3 100644 --- a/src/libsysprof-analyze/sysprof-elf-loader.c +++ b/src/libsysprof-analyze/sysprof-elf-loader.c @@ -29,6 +29,7 @@ struct _SysprofElfLoader { GObject parent_instance; + GHashTable *cache; char **debug_dirs; char **external_debug_dirs; }; @@ -59,6 +60,7 @@ sysprof_elf_loader_finalize (GObject *object) g_clear_pointer (&self->debug_dirs, g_strfreev); g_clear_pointer (&self->external_debug_dirs, g_strfreev); + g_clear_pointer (&self->cache, g_hash_table_unref); G_OBJECT_CLASS (sysprof_elf_loader_parent_class)->finalize (object); } @@ -138,6 +140,7 @@ static void sysprof_elf_loader_init (SysprofElfLoader *self) { self->debug_dirs = g_strdupv ((char **)DEFAULT_DEBUG_DIRS); + self->cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); } /** @@ -299,12 +302,16 @@ sysprof_elf_loader_load (SysprofElfLoader *self, g_autoptr(SysprofElf) elf = NULL; g_autoptr(GError) local_error = NULL; g_autofree char *container_path = NULL; + SysprofElf *cached_elf; const char *path = paths[i]; const char *debug_link; if ((in_flatpak && !g_str_has_prefix (path, "/home/")) || in_podman) path = container_path = g_build_filename ("/var/run/host", path, NULL); + if ((cached_elf = g_hash_table_lookup (self->cache, path))) + return g_object_ref (cached_elf); + if (!(mapped_file = g_mapped_file_new (path, FALSE, NULL))) continue; @@ -314,6 +321,8 @@ sysprof_elf_loader_load (SysprofElfLoader *self, if ((debug_link = sysprof_elf_get_debug_link (elf))) sysprof_elf_loader_annotate (self, mount_namespace, elf, debug_link); + g_hash_table_insert (self->cache, g_strdup (path), g_object_ref (elf)); + return g_steal_pointer (&elf); } From d48f2763e0df0f52f4b217d4d54ab63409f16543 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 19 May 2023 13:58:25 -0700 Subject: [PATCH 0168/1030] libsysprof-analyze: cache negative lookups If we fail to parse something as an ELF, then make sure we can skip it the next time we see it. --- src/libsysprof-analyze/sysprof-elf-loader.c | 38 ++++++++++++++------- 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-elf-loader.c b/src/libsysprof-analyze/sysprof-elf-loader.c index e466d8b3..965e795b 100644 --- a/src/libsysprof-analyze/sysprof-elf-loader.c +++ b/src/libsysprof-analyze/sysprof-elf-loader.c @@ -53,6 +53,13 @@ sysprof_elf_loader_new (void) return g_object_new (SYSPROF_TYPE_ELF_LOADER, NULL); } +static void +_g_object_xunref (gpointer data) +{ + if (data != NULL) + g_object_unref (data); +} + static void sysprof_elf_loader_finalize (GObject *object) { @@ -140,7 +147,7 @@ static void sysprof_elf_loader_init (SysprofElfLoader *self) { self->debug_dirs = g_strdupv ((char **)DEFAULT_DEBUG_DIRS); - self->cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); + self->cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, _g_object_xunref); } /** @@ -302,26 +309,33 @@ sysprof_elf_loader_load (SysprofElfLoader *self, g_autoptr(SysprofElf) elf = NULL; g_autoptr(GError) local_error = NULL; g_autofree char *container_path = NULL; - SysprofElf *cached_elf; + SysprofElf *cached_elf = NULL; const char *path = paths[i]; const char *debug_link; if ((in_flatpak && !g_str_has_prefix (path, "/home/")) || in_podman) path = container_path = g_build_filename ("/var/run/host", path, NULL); - if ((cached_elf = g_hash_table_lookup (self->cache, path))) - return g_object_ref (cached_elf); + /* Lookup to see if we've already parsed this ELF and handle cases where + * we've failed to load it too. In the case we failed to load a key is + * stored in the cache with a NULL value. + */ + if (g_hash_table_lookup_extended (self->cache, path, NULL, (gpointer *)&cached_elf)) + return cached_elf ? g_object_ref (cached_elf) : NULL; - if (!(mapped_file = g_mapped_file_new (path, FALSE, NULL))) - continue; - - if (!(elf = sysprof_elf_new (path, g_steal_pointer (&mapped_file), &local_error))) - continue; - - if ((debug_link = sysprof_elf_get_debug_link (elf))) + /* Try to mmap the file and parse it. If the parser fails to parse the + * section headers, then this probably isn't an ELF file and we should + * store a failure record in the cache so that we don't attempt to load + * it again. + */ + if ((mapped_file = g_mapped_file_new (path, FALSE, NULL)) && + (elf = sysprof_elf_new (path, g_steal_pointer (&mapped_file), &local_error)) && + (debug_link = sysprof_elf_get_debug_link (elf))) sysprof_elf_loader_annotate (self, mount_namespace, elf, debug_link); - g_hash_table_insert (self->cache, g_strdup (path), g_object_ref (elf)); + g_hash_table_insert (self->cache, + g_strdup (path), + elf ? g_object_ref (elf) : NULL); return g_steal_pointer (&elf); } From 7885873ba3f8d6a65aea49fa20cf7ec609d8aacc Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 19 May 2023 14:52:49 -0700 Subject: [PATCH 0169/1030] libsysprof-analyze: implement various SysprofElf API This wraps our ElfParser to give us an idiomatic API we can use with GIO/GTK/etc. --- src/libsysprof-analyze/sysprof-elf-private.h | 2 +- src/libsysprof-analyze/sysprof-elf.c | 41 ++++++++++++++++---- 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-elf-private.h b/src/libsysprof-analyze/sysprof-elf-private.h index 95a1d51b..12775271 100644 --- a/src/libsysprof-analyze/sysprof-elf-private.h +++ b/src/libsysprof-analyze/sysprof-elf-private.h @@ -34,7 +34,7 @@ SysprofElf *sysprof_elf_new (const char *filename, const char *sysprof_elf_get_file (SysprofElf *self); const char *sysprof_elf_get_build_id (SysprofElf *self); const char *sysprof_elf_get_debug_link (SysprofElf *self); -const char *sysprof_elf_get_symbol_at_address (SysprofElf *self, +char *sysprof_elf_get_symbol_at_address (SysprofElf *self, guint64 address, guint64 *begin_address, guint64 *end_address); diff --git a/src/libsysprof-analyze/sysprof-elf.c b/src/libsysprof-analyze/sysprof-elf.c index 1cc50502..9a241e8c 100644 --- a/src/libsysprof-analyze/sysprof-elf.c +++ b/src/libsysprof-analyze/sysprof-elf.c @@ -28,7 +28,6 @@ struct _SysprofElf { GObject parent_instance; char *build_id; - char *debug_link; char *file; SysprofElf *debug_link_elf; ElfParser *parser; @@ -53,7 +52,6 @@ sysprof_elf_finalize (GObject *object) SysprofElf *self = (SysprofElf *)object; g_clear_pointer (&self->build_id, g_free); - g_clear_pointer (&self->debug_link, g_free); g_clear_pointer (&self->file, g_free); g_clear_pointer (&self->parser, elf_parser_free); g_clear_object (&self->debug_link_elf); @@ -187,24 +185,51 @@ sysprof_elf_get_build_id (SysprofElf *self) const char * sysprof_elf_get_debug_link (SysprofElf *self) { + guint crc32; + g_return_val_if_fail (SYSPROF_IS_ELF (self), NULL); - return self->debug_link; + return elf_parser_get_debug_link (self->parser, &crc32); } -const char * +char * sysprof_elf_get_symbol_at_address (SysprofElf *self, guint64 address, guint64 *begin_address, guint64 *end_address) { + const ElfSym *symbol; + char *ret = NULL; + gulong begin = 0; + gulong end = 0; + g_return_val_if_fail (SYSPROF_IS_ELF (self), NULL); - g_return_val_if_fail (begin_address != NULL, NULL); - g_return_val_if_fail (end_address != NULL, NULL); - *begin_address = *end_address = 0; + if (self->debug_link_elf != NULL) + return sysprof_elf_get_symbol_at_address (self->debug_link_elf, address, begin_address, end_address); - return NULL; + if ((symbol = elf_parser_lookup_symbol (self->parser, address))) + { + const char *name; + + if (begin_address || end_address) + elf_parser_get_sym_address_range (self->parser, symbol, &begin, &end); + + name = elf_parser_get_sym_name (self->parser, symbol); + + if (name != NULL && name[0] == '_' && name[1] == 'Z') + ret = elf_demangle (name); + else + ret = g_strdup (name); + } + + if (begin_address) + *begin_address = begin; + + if (end_address) + *end_address = end; + + return ret; } /** From c59bb4ca15facb3e0417ed7371fe9c577129d519 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 19 May 2023 14:53:06 -0700 Subject: [PATCH 0170/1030] libsysprof-analyze: implement ELF debuglink loading --- src/libsysprof-analyze/sysprof-elf-loader.c | 53 ++++++++++++++------- 1 file changed, 37 insertions(+), 16 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-elf-loader.c b/src/libsysprof-analyze/sysprof-elf-loader.c index 965e795b..6c40fb34 100644 --- a/src/libsysprof-analyze/sysprof-elf-loader.c +++ b/src/libsysprof-analyze/sysprof-elf-loader.c @@ -244,27 +244,43 @@ sysprof_elf_loader_set_external_debug_dirs (SysprofElfLoader *self, g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_EXTERNAL_DEBUG_DIRS]); } +static char * +access_path_from_container (const char *path) +{ + if ((in_flatpak && !g_str_has_prefix (path, "/home/")) || in_podman) + return g_build_filename ("/var/run/host", path, NULL); + return g_strdup (path); +} + +static SysprofElf * +get_deepest_debuglink (SysprofElf *elf) +{ + SysprofElf *debug_link = sysprof_elf_get_debug_link_elf (elf); + return debug_link ? get_deepest_debuglink (debug_link) : elf; +} + static void sysprof_elf_loader_annotate (SysprofElfLoader *self, SysprofMountNamespace *mount_namespace, + const char *orig_file, SysprofElf *elf, const char *debug_link) { + g_autoptr(SysprofElf) debug_link_elf = NULL; + g_autofree char *directory_name = NULL; + g_autofree char *debug_path = NULL; + g_autofree char *container_path = NULL; + g_assert (SYSPROF_IS_ELF_LOADER (self)); g_assert (SYSPROF_IS_MOUNT_NAMESPACE (mount_namespace)); g_assert (SYSPROF_IS_ELF (elf)); g_assert (debug_link != NULL); - /* TODO: We want to use the debug_link to find a similar file that will - * contain the various debugsymbols. We need to look for it in - * any of the #SysprofElfLoader:debug-dirs (and translated via the - * mount namespace) as well as any of the :external_debug_dirs - * which is a path available to us from our application's mount - * namespace. We recursively follow those debug_link (and must - * protect against cycles) to get the final/best debuglink file. - * That will get assigned via sysprof_elf_set_debug_link_elf(). - */ + directory_name = g_path_get_dirname (orig_file); + debug_path = g_build_filename ("/usr/lib/debug", directory_name, debug_link, NULL); + if ((debug_link_elf = sysprof_elf_loader_load (self, mount_namespace, debug_path, NULL, NULL))) + sysprof_elf_set_debug_link_elf (elf, get_deepest_debuglink (debug_link_elf)); } /** @@ -313,30 +329,35 @@ sysprof_elf_loader_load (SysprofElfLoader *self, const char *path = paths[i]; const char *debug_link; - if ((in_flatpak && !g_str_has_prefix (path, "/home/")) || in_podman) - path = container_path = g_build_filename ("/var/run/host", path, NULL); + if (in_flatpak || in_podman) + path = container_path = access_path_from_container (path); /* Lookup to see if we've already parsed this ELF and handle cases where * we've failed to load it too. In the case we failed to load a key is * stored in the cache with a NULL value. */ if (g_hash_table_lookup_extended (self->cache, path, NULL, (gpointer *)&cached_elf)) - return cached_elf ? g_object_ref (cached_elf) : NULL; + { + if (cached_elf != NULL) + return g_object_ref (cached_elf); + continue; + } /* Try to mmap the file and parse it. If the parser fails to parse the * section headers, then this probably isn't an ELF file and we should * store a failure record in the cache so that we don't attempt to load * it again. */ - if ((mapped_file = g_mapped_file_new (path, FALSE, NULL)) && - (elf = sysprof_elf_new (path, g_steal_pointer (&mapped_file), &local_error)) && - (debug_link = sysprof_elf_get_debug_link (elf))) - sysprof_elf_loader_annotate (self, mount_namespace, elf, debug_link); + if ((mapped_file = g_mapped_file_new (path, FALSE, NULL))) + elf = sysprof_elf_new (path, g_steal_pointer (&mapped_file), &local_error); g_hash_table_insert (self->cache, g_strdup (path), elf ? g_object_ref (elf) : NULL); + if (elf && (debug_link = sysprof_elf_get_debug_link (elf))) + sysprof_elf_loader_annotate (self, mount_namespace, file, elf, debug_link); + return g_steal_pointer (&elf); } From f1f02614868f158dd1f4b3ded0a506243e6e7365 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 19 May 2023 15:33:53 -0700 Subject: [PATCH 0171/1030] libsysprof-analyze: check if ELF matches inode/build-id We still want to load it into the cache as it could get used by other symbols/mmap regions, but don't return the ELF if it won't match an inode or build-id check. Rely on other fallbacks to create fallback symbols for those use cases. --- .../sysprof-elf-loader-private.h | 1 + src/libsysprof-analyze/sysprof-elf-loader.c | 23 ++++++++++++++++--- src/libsysprof-analyze/sysprof-elf-private.h | 3 +++ src/libsysprof-analyze/sysprof-elf.c | 23 +++++++++++++++++++ .../tests/test-elf-loader.c | 3 ++- 5 files changed, 49 insertions(+), 4 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-elf-loader-private.h b/src/libsysprof-analyze/sysprof-elf-loader-private.h index bfc0d74b..363ca751 100644 --- a/src/libsysprof-analyze/sysprof-elf-loader-private.h +++ b/src/libsysprof-analyze/sysprof-elf-loader-private.h @@ -43,6 +43,7 @@ SysprofElf *sysprof_elf_loader_load (SysprofElfLoader SysprofMountNamespace *mount_namespace, const char *file, const char *build_id, + guint64 file_inode, GError **error); diff --git a/src/libsysprof-analyze/sysprof-elf-loader.c b/src/libsysprof-analyze/sysprof-elf-loader.c index 6c40fb34..c0e0396a 100644 --- a/src/libsysprof-analyze/sysprof-elf-loader.c +++ b/src/libsysprof-analyze/sysprof-elf-loader.c @@ -270,6 +270,7 @@ sysprof_elf_loader_annotate (SysprofElfLoader *self, g_autofree char *directory_name = NULL; g_autofree char *debug_path = NULL; g_autofree char *container_path = NULL; + const char *build_id; g_assert (SYSPROF_IS_ELF_LOADER (self)); g_assert (SYSPROF_IS_MOUNT_NAMESPACE (mount_namespace)); @@ -278,8 +279,9 @@ sysprof_elf_loader_annotate (SysprofElfLoader *self, directory_name = g_path_get_dirname (orig_file); debug_path = g_build_filename ("/usr/lib/debug", directory_name, debug_link, NULL); + build_id = sysprof_elf_get_build_id (elf); - if ((debug_link_elf = sysprof_elf_loader_load (self, mount_namespace, debug_path, NULL, NULL))) + if ((debug_link_elf = sysprof_elf_loader_load (self, mount_namespace, debug_path, build_id, 0, NULL))) sysprof_elf_set_debug_link_elf (elf, get_deepest_debuglink (debug_link_elf)); } @@ -290,6 +292,8 @@ sysprof_elf_loader_annotate (SysprofElfLoader *self, * @file: the path of the file to load within the mount namespace * @build_id: (nullable): an optional build-id that can be used to resolve * the file alternatively to the file path + * @file_inode: expected inode for @file + * @error: a location for a #GError, or %NULL * * Attempts to load a #SysprofElf for @file (or optionally by @build_id). * @@ -305,6 +309,7 @@ sysprof_elf_loader_load (SysprofElfLoader *self, SysprofMountNamespace *mount_namespace, const char *file, const char *build_id, + guint64 file_inode, GError **error) { g_auto(GStrv) paths = NULL; @@ -355,8 +360,20 @@ sysprof_elf_loader_load (SysprofElfLoader *self, g_strdup (path), elf ? g_object_ref (elf) : NULL); - if (elf && (debug_link = sysprof_elf_get_debug_link (elf))) - sysprof_elf_loader_annotate (self, mount_namespace, file, elf, debug_link); + if (elf != NULL) + { + if ((debug_link = sysprof_elf_get_debug_link (elf))) + sysprof_elf_loader_annotate (self, mount_namespace, file, elf, debug_link); + + /* If we loaded the ELF, but it doesn't match what this request is looking + * for in terms of inode/build-id, then we need to bail and not return it. + * We can, however, leave it in the cache incase another process/sample + * will need the ELF. + */ + if (!sysprof_elf_matches (elf, file_inode, build_id)) + g_clear_object (&elf); + } + return g_steal_pointer (&elf); } diff --git a/src/libsysprof-analyze/sysprof-elf-private.h b/src/libsysprof-analyze/sysprof-elf-private.h index 12775271..7dda3a36 100644 --- a/src/libsysprof-analyze/sysprof-elf-private.h +++ b/src/libsysprof-analyze/sysprof-elf-private.h @@ -31,6 +31,9 @@ G_DECLARE_FINAL_TYPE (SysprofElf, sysprof_elf, SYSPROF, ELF, GObject) SysprofElf *sysprof_elf_new (const char *filename, GMappedFile *mapped_file, GError **error); +gboolean sysprof_elf_matches (SysprofElf *self, + guint64 file_inode, + const char *build_id); const char *sysprof_elf_get_file (SysprofElf *self); const char *sysprof_elf_get_build_id (SysprofElf *self); const char *sysprof_elf_get_debug_link (SysprofElf *self); diff --git a/src/libsysprof-analyze/sysprof-elf.c b/src/libsysprof-analyze/sysprof-elf.c index 9a241e8c..c19cb080 100644 --- a/src/libsysprof-analyze/sysprof-elf.c +++ b/src/libsysprof-analyze/sysprof-elf.c @@ -31,6 +31,7 @@ struct _SysprofElf char *file; SysprofElf *debug_link_elf; ElfParser *parser; + guint64 file_inode; }; enum { @@ -259,3 +260,25 @@ sysprof_elf_set_debug_link_elf (SysprofElf *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]); } + +gboolean +sysprof_elf_matches (SysprofElf *self, + guint64 file_inode, + const char *build_id) +{ + g_return_val_if_fail (SYSPROF_IS_ELF (self), FALSE); + + if (build_id != NULL) + { + const char *elf_build_id = elf_parser_get_build_id (self->parser); + + /* Not matching build-id, you definitely don't want this ELF */ + if (elf_build_id != NULL && !g_str_equal (build_id, elf_build_id)) + return FALSE; + } + + if (file_inode && self->file_inode && file_inode != self->file_inode) + return FALSE; + + return TRUE; +} diff --git a/src/libsysprof-analyze/tests/test-elf-loader.c b/src/libsysprof-analyze/tests/test-elf-loader.c index 73616e0b..e1c0c67d 100644 --- a/src/libsysprof-analyze/tests/test-elf-loader.c +++ b/src/libsysprof-analyze/tests/test-elf-loader.c @@ -69,7 +69,8 @@ main (int argc, g_autoptr(SysprofDocumentMmap) memory_map = g_list_model_get_item (memory_maps, j); const char *file = sysprof_document_mmap_get_file (memory_map); const char *build_id = sysprof_document_mmap_get_build_id (memory_map); - g_autoptr(SysprofElf) elf = sysprof_elf_loader_load (elf_loader, info->mount_namespace, file, build_id, &error); + guint64 file_inode = sysprof_document_mmap_get_file_inode (memory_map); + g_autoptr(SysprofElf) elf = sysprof_elf_loader_load (elf_loader, info->mount_namespace, file, build_id, file_inode, &error); if (elf == NULL) g_print ("%u: %s [unresolved]\n", info->pid, file); From 1833fabd6235480a364aa58bb550c426f4c532f1 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 19 May 2023 15:44:26 -0700 Subject: [PATCH 0172/1030] libsysprof-analyze: include inode when creating ELF We want to know the inode of the FD that was mmaped so that we can check the requested inode when processing the address map from a particular process. --- src/libsysprof-analyze/sysprof-elf-loader.c | 35 ++++++++++++++++++-- src/libsysprof-analyze/sysprof-elf-private.h | 1 + src/libsysprof-analyze/sysprof-elf.c | 2 ++ 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-elf-loader.c b/src/libsysprof-analyze/sysprof-elf-loader.c index c0e0396a..cb0d9363 100644 --- a/src/libsysprof-analyze/sysprof-elf-loader.c +++ b/src/libsysprof-analyze/sysprof-elf-loader.c @@ -20,6 +20,9 @@ #include "config.h" +#include +#include + #include "sysprof-elf-private.h" #include "sysprof-elf-loader-private.h" #include "sysprof-strings-private.h" @@ -285,6 +288,33 @@ sysprof_elf_loader_annotate (SysprofElfLoader *self, sysprof_elf_set_debug_link_elf (elf, get_deepest_debuglink (debug_link_elf)); } +static gboolean +get_file_and_inode (const char *path, + GMappedFile **mapped_file, + guint64 *file_inode) +{ + struct stat stbuf; + int fd; + + g_assert (path != NULL); + g_assert (mapped_file != NULL); + g_assert (file_inode != NULL); + + *mapped_file = NULL; + *file_inode = 0; + + fd = open (path, O_RDONLY|O_CLOEXEC, 0); + if (fd == -1) + return FALSE; + + if (fstat (fd, &stbuf) == 0) + *file_inode = (guint64)stbuf.st_ino; + *mapped_file = g_mapped_file_new_from_fd (fd, FALSE, NULL); + close (fd); + + return *mapped_file != NULL; +} + /** * sysprof_elf_loader_load: * @self: a #SysprofElfLoader @@ -333,6 +363,7 @@ sysprof_elf_loader_load (SysprofElfLoader *self, SysprofElf *cached_elf = NULL; const char *path = paths[i]; const char *debug_link; + guint64 mapped_file_inode; if (in_flatpak || in_podman) path = container_path = access_path_from_container (path); @@ -353,8 +384,8 @@ sysprof_elf_loader_load (SysprofElfLoader *self, * store a failure record in the cache so that we don't attempt to load * it again. */ - if ((mapped_file = g_mapped_file_new (path, FALSE, NULL))) - elf = sysprof_elf_new (path, g_steal_pointer (&mapped_file), &local_error); + if (get_file_and_inode (path, &mapped_file, &mapped_file_inode)) + elf = sysprof_elf_new (path, g_steal_pointer (&mapped_file), mapped_file_inode, &local_error); g_hash_table_insert (self->cache, g_strdup (path), diff --git a/src/libsysprof-analyze/sysprof-elf-private.h b/src/libsysprof-analyze/sysprof-elf-private.h index 7dda3a36..e8d5ac92 100644 --- a/src/libsysprof-analyze/sysprof-elf-private.h +++ b/src/libsysprof-analyze/sysprof-elf-private.h @@ -30,6 +30,7 @@ G_DECLARE_FINAL_TYPE (SysprofElf, sysprof_elf, SYSPROF, ELF, GObject) SysprofElf *sysprof_elf_new (const char *filename, GMappedFile *mapped_file, + guint64 file_inode, GError **error); gboolean sysprof_elf_matches (SysprofElf *self, guint64 file_inode, diff --git a/src/libsysprof-analyze/sysprof-elf.c b/src/libsysprof-analyze/sysprof-elf.c index c19cb080..ac4e66bd 100644 --- a/src/libsysprof-analyze/sysprof-elf.c +++ b/src/libsysprof-analyze/sysprof-elf.c @@ -150,6 +150,7 @@ sysprof_elf_init (SysprofElf *self) SysprofElf * sysprof_elf_new (const char *filename, GMappedFile *mapped_file, + guint64 file_inode, GError **error) { SysprofElf *self; @@ -163,6 +164,7 @@ sysprof_elf_new (const char *filename, self = g_object_new (SYSPROF_TYPE_ELF, NULL); self->file = g_strdup (filename); self->parser = g_steal_pointer (&parser); + self->file_inode = file_inode; return self; } From 167255be458d033e8040ba843da0776a12959d3b Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 19 May 2023 15:53:41 -0700 Subject: [PATCH 0173/1030] libsysprof-analyze: implement basics of ELF symbolizer There are still lots of kinks to iron out of this, but it gets some of the basic plumbing in place for symbolizing. Particularly, we're not at all yet addressing the overlays in the capture which will be needed to do some handling of Flatpak/Podman processes. Basic build-id/file-inode checks are done, but we just return NULL in those cases (unlike previously in Sysprof where we would say "Inode Mismatch". In those cases the fallback path is hit now which will just give a file path plus instruction-pointer offset. We can show more details though in the future now that we have more objects to represent things. --- .../sysprof-elf-symbolizer.c | 94 +++++++++---------- 1 file changed, 42 insertions(+), 52 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-elf-symbolizer.c b/src/libsysprof-analyze/sysprof-elf-symbolizer.c index fd1aa257..14a26fdb 100644 --- a/src/libsysprof-analyze/sysprof-elf-symbolizer.c +++ b/src/libsysprof-analyze/sysprof-elf-symbolizer.c @@ -20,8 +20,8 @@ #include "config.h" -#include "../libsysprof/binfile.h" - +#include "sysprof-elf-private.h" +#include "sysprof-elf-loader-private.h" #include "sysprof-elf-symbolizer.h" #include "sysprof-document-private.h" #include "sysprof-strings-private.h" @@ -31,7 +31,7 @@ struct _SysprofElfSymbolizer { SysprofSymbolizer parent_instance; - GHashTable *bin_file_cache; + SysprofElfLoader *loader; }; struct _SysprofElfSymbolizerClass @@ -49,12 +49,17 @@ sysprof_elf_symbolizer_symbolize (SysprofSymbolizer *symbolizer, SysprofAddress address) { SysprofElfSymbolizer *self = (SysprofElfSymbolizer *)symbolizer; + g_autoptr(SysprofElf) elf = NULL; SysprofDocumentMmap *map; g_autofree char *name = NULL; - g_auto(GStrv) translations = NULL; - GMappedFile *mapped_file = NULL; - guint64 relative_address; const char *path; + const char *build_id; + guint64 start_address; + guint64 relative_address; + guint64 begin_address; + guint64 end_address; + guint64 file_inode; + guint64 file_offset; if (process_info == NULL || process_info->address_layout == NULL || @@ -67,63 +72,51 @@ sysprof_elf_symbolizer_symbolize (SysprofSymbolizer *symbolizer, if (!(map = sysprof_address_layout_lookup (process_info->address_layout, address))) return NULL; - /* The file could be available at a number of locations in case there - * is an overlayfs, flatpak runtime, etc. Additionally, we may need - * to resolve through various debug directories. All of those also - * need to get translated to a location where this process can access - * then (which itself might be via /var/run/host/... or similar). - */ path = sysprof_document_mmap_get_file (map); + build_id = sysprof_document_mmap_get_build_id (map); + file_inode = sysprof_document_mmap_get_file_inode (map); + file_offset = sysprof_document_mmap_get_file_offset (map); + start_address = sysprof_document_mmap_get_start_address (map); + relative_address = file_offset + (address - start_address); - /* TODO: - * - * We need something like bin_file_t here that will let us look at - * all of our possible translations for the file (overlayfs, etc) - * and add debug directories on top of that. The debug directories - * can be used to follow .gnu_debuglink through debug dirs. + /* See if we can load an ELF at the path . It will be translated from the + * mount namespace into something hopefully we can access. */ - -#if 0 - if (!(translations = sysprof_mount_namespace_translate (process_info->mount_namespace, path))) + if (!(elf = sysprof_elf_loader_load (self->loader, + process_info->mount_namespace, + path, + build_id, + file_inode, + NULL))) goto fallback; - for (guint i = 0; translations[i]; i++) - { - /* If the file exists within our cache already, re-use that instead - * of re-opening a binfile. - */ - if ((mapped_file = g_hash_table_lookup (self->bin_file_cache, translations[i]))) - break; - - if ((mapped_file = g_mapped_file_new (translations[i], FALSE, NULL))) - { - g_hash_table_insert (self->bin_file_cache, - g_strdup (translations[i]), - mapped_file); - break; - } - } - - if (mapped_file == NULL) + /* 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))) goto fallback; -#endif + + return _sysprof_symbol_new (sysprof_strings_get (strings, name), + NULL, + sysprof_strings_get (strings, path), + start_address + (begin_address - file_offset), + start_address + (end_address - file_offset)); 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. */ - relative_address = sysprof_document_mmap_get_file_offset (map) - + (address - sysprof_document_mmap_get_start_address (map)); 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; return _sysprof_symbol_new (sysprof_strings_get (strings, name), - NULL, - NULL, - address, - address + 1); + NULL, NULL, begin_address, end_address); } static void @@ -131,7 +124,7 @@ sysprof_elf_symbolizer_finalize (GObject *object) { SysprofElfSymbolizer *self = (SysprofElfSymbolizer *)object; - g_clear_pointer (&self->bin_file_cache, g_hash_table_unref); + g_clear_object (&self->loader); G_OBJECT_CLASS (sysprof_elf_symbolizer_parent_class)->finalize (object); } @@ -150,10 +143,7 @@ sysprof_elf_symbolizer_class_init (SysprofElfSymbolizerClass *klass) static void sysprof_elf_symbolizer_init (SysprofElfSymbolizer *self) { - self->bin_file_cache = g_hash_table_new_full (g_str_hash, - g_str_equal, - g_free, - (GDestroyNotify)bin_file_free); + self->loader = sysprof_elf_loader_new (); } SysprofSymbolizer * From d7f6e4b92201c8d8123dbc3e834d39480d48551f Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 19 May 2023 16:18:06 -0700 Subject: [PATCH 0174/1030] libsysprof-analyze: add some common tags It's nice to have various labels on subsystems used on the desktop, so add some common ones here that are part of the platform. --- src/libsysprof-analyze/sysprof-elf-private.h | 1 + .../sysprof-elf-symbolizer.c | 2 +- src/libsysprof-analyze/sysprof-elf.c | 93 +++++++++++++++++++ 3 files changed, 95 insertions(+), 1 deletion(-) diff --git a/src/libsysprof-analyze/sysprof-elf-private.h b/src/libsysprof-analyze/sysprof-elf-private.h index e8d5ac92..146bc2de 100644 --- a/src/libsysprof-analyze/sysprof-elf-private.h +++ b/src/libsysprof-analyze/sysprof-elf-private.h @@ -35,6 +35,7 @@ SysprofElf *sysprof_elf_new (const char *filename, gboolean sysprof_elf_matches (SysprofElf *self, guint64 file_inode, const char *build_id); +const char *sysprof_elf_get_nick (SysprofElf *self); const char *sysprof_elf_get_file (SysprofElf *self); const char *sysprof_elf_get_build_id (SysprofElf *self); const char *sysprof_elf_get_debug_link (SysprofElf *self); diff --git a/src/libsysprof-analyze/sysprof-elf-symbolizer.c b/src/libsysprof-analyze/sysprof-elf-symbolizer.c index 14a26fdb..5994219e 100644 --- a/src/libsysprof-analyze/sysprof-elf-symbolizer.c +++ b/src/libsysprof-analyze/sysprof-elf-symbolizer.c @@ -100,7 +100,7 @@ sysprof_elf_symbolizer_symbolize (SysprofSymbolizer *symbolizer, goto fallback; return _sysprof_symbol_new (sysprof_strings_get (strings, name), - NULL, + sysprof_strings_get (strings, sysprof_elf_get_nick (elf)), sysprof_strings_get (strings, path), start_address + (begin_address - file_offset), start_address + (end_address - file_offset)); diff --git a/src/libsysprof-analyze/sysprof-elf.c b/src/libsysprof-analyze/sysprof-elf.c index ac4e66bd..54b5e493 100644 --- a/src/libsysprof-analyze/sysprof-elf.c +++ b/src/libsysprof-analyze/sysprof-elf.c @@ -27,6 +27,7 @@ struct _SysprofElf { GObject parent_instance; + const char *nick; char *build_id; char *file; SysprofElf *debug_link_elf; @@ -46,6 +47,51 @@ enum { G_DEFINE_FINAL_TYPE (SysprofElf, sysprof_elf, G_TYPE_OBJECT) static GParamSpec *properties [N_PROPS]; +static GHashTable *nicks; +static const struct { + const char *library; + const char *nick; +} nick_table[] = { + { "libEGL.so", "EGL" }, + { "libEGL_mesa.so", "Mesa EGL" }, + { "libGL.so", "GL" }, + { "libX11-xcb.so", "X11" }, + { "libX11.so", "X11" }, + { "libc.so", "libc" }, + { "libcairo-gobject.so", "Cairo" }, + { "libcairo.so", "Cairo" }, + { "libclutter-1.0.so", "Clutter" }, + { "libclutter-glx-1.0.so", "Clutter" }, + { "libffi.so", "libffi" }, + { "libgdk-3.so", "GDK 3" }, + { "libgio-2.0.so", "Gio" }, + { "libgirepository-1.0.so", "Introspection" }, + { "libgjs.so", "GJS" }, + { "libglib-2.0.so", "GLib" }, + { "libgobject-2.0.so", "GObject" }, + { "libgstreamer-1-0.so", "GStreamer" }, + { "libgtk-3.so", "GTK 3" }, + { "libgtk-4.so", "GTK 4" }, + { "libgtksourceview-3.0.so", "GtkSourceView 3" }, + { "libgtksourceview-4.so", "GtkSourceView 4" }, + { "libgtksourceview-5.so", "GtkSourceView 5" }, + { "libharfbuzz-cairo.so", "Harfbuzz" }, + { "libharfbuzz-gobject.so", "Harfbuzz" }, + { "libharfbuzz-icu.so", "Harfbuzz" }, + { "libharfbuzz-subset.so", "Harfbuzz" }, + { "libharfbuzz.so", "Harfbuzz" }, + { "libinput.so", "Mutter" }, + { "libmutter-12.so", "Mutter" }, + { "libpango-1.0.so", "Pango" }, + { "libpangocairo-1.0.so", "Pango" }, + { "libpipewire-0.3.so", "Pipewire" }, + { "libpixman-1.so", "Pixman" }, + { "libstdc++.so", "libc" }, + { "libwayland-client.so", "Wayland Client" }, + { "libwayland-cursor.so", "Wayland Cursor" }, + { "libwayland-egl.so", "Wayland EGL" }, + { "libwayland-server.so", "Wayland Server" }, +}; static void sysprof_elf_finalize (GObject *object) @@ -140,6 +186,12 @@ sysprof_elf_class_init (SysprofElfClass *klass) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_object_class_install_properties (object_class, N_PROPS, properties); + + nicks = g_hash_table_new (g_str_hash, g_str_equal); + for (guint i = 0; i < G_N_ELEMENTS (nick_table); i++) + g_hash_table_insert (nicks, + (char *)nick_table[i].library, + (char *)nick_table[i].nick); } static void @@ -147,6 +199,25 @@ sysprof_elf_init (SysprofElf *self) { } +static void +guess_nick (SysprofElf *self, + const char *name, + const char *endptr) +{ + char key[32]; + + if (endptr <= name) + return; + + if (endptr - name >= sizeof key) + return; + + memcpy (key, name, endptr-name); + key[endptr-name] = 0; + + self->nick = g_hash_table_lookup (nicks, key); +} + SysprofElf * sysprof_elf_new (const char *filename, GMappedFile *mapped_file, @@ -166,6 +237,20 @@ sysprof_elf_new (const char *filename, self->parser = g_steal_pointer (&parser); self->file_inode = file_inode; + if (filename != NULL) + { + const char *base; + const char *endptr; + + if ((base = strrchr (filename, '/'))) + { + endptr = strstr (++base, ".so"); + + if (endptr != NULL && (endptr[3] == 0 || endptr[3] == '.')) + guess_nick (self, base, &endptr[3]); + } + } + return self; } @@ -284,3 +369,11 @@ sysprof_elf_matches (SysprofElf *self, return TRUE; } + +const char * +sysprof_elf_get_nick (SysprofElf *self) +{ + g_return_val_if_fail (SYSPROF_IS_ELF (self), NULL); + + return self->nick; +} From 61d5187b0953dc3635b7842a106fb159e147cb33 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 19 May 2023 16:33:21 -0700 Subject: [PATCH 0175/1030] libsysprof-analyze: fallback in case of debuglink error If for some reason the debuglink has an issue, we can fallback to looking at the specific ELF containing the address we wanted to look at. --- src/libsysprof-analyze/sysprof-elf.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/libsysprof-analyze/sysprof-elf.c b/src/libsysprof-analyze/sysprof-elf.c index 54b5e493..a5f0a91f 100644 --- a/src/libsysprof-analyze/sysprof-elf.c +++ b/src/libsysprof-analyze/sysprof-elf.c @@ -294,7 +294,12 @@ sysprof_elf_get_symbol_at_address (SysprofElf *self, g_return_val_if_fail (SYSPROF_IS_ELF (self), NULL); if (self->debug_link_elf != NULL) - return sysprof_elf_get_symbol_at_address (self->debug_link_elf, address, begin_address, end_address); + { + ret = sysprof_elf_get_symbol_at_address (self->debug_link_elf, address, begin_address, end_address); + + if (ret != NULL) + return ret; + } if ((symbol = elf_parser_lookup_symbol (self->parser, address))) { From 12e497891ff080b6f66e16e1b685363b7623c7a4 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 19 May 2023 16:51:15 -0700 Subject: [PATCH 0176/1030] libsysprof-analyze: add SysprofDocumentOverlay This maps to the SysprofCaptureOverlay data frames. We need them currently to be able to locate files from various podman/toolbox rootless containers. --- src/libsysprof-analyze/meson.build | 2 + src/libsysprof-analyze/sysprof-analyze.h | 1 + .../sysprof-document-frame.c | 5 + .../sysprof-document-overlay.c | 142 ++++++++++++++++++ .../sysprof-document-overlay.h | 47 ++++++ .../tests/test-capture-model.c | 5 + 6 files changed, 202 insertions(+) create mode 100644 src/libsysprof-analyze/sysprof-document-overlay.c create mode 100644 src/libsysprof-analyze/sysprof-document-overlay.h diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build index 7f9e4197..3b5f72c6 100644 --- a/src/libsysprof-analyze/meson.build +++ b/src/libsysprof-analyze/meson.build @@ -12,6 +12,7 @@ libsysprof_analyze_public_sources = [ 'sysprof-document-mark.c', 'sysprof-document-metadata.c', 'sysprof-document-mmap.c', + 'sysprof-document-overlay.c', 'sysprof-document-process.c', 'sysprof-document-sample.c', 'sysprof-document-traceable.c', @@ -55,6 +56,7 @@ libsysprof_analyze_public_headers = [ 'sysprof-document-mark.h', 'sysprof-document-metadata.h', 'sysprof-document-mmap.h', + 'sysprof-document-overlay.h', 'sysprof-document-process.h', 'sysprof-document-sample.h', 'sysprof-document-traceable.h', diff --git a/src/libsysprof-analyze/sysprof-analyze.h b/src/libsysprof-analyze/sysprof-analyze.h index b1fb7b39..3e2b186d 100644 --- a/src/libsysprof-analyze/sysprof-analyze.h +++ b/src/libsysprof-analyze/sysprof-analyze.h @@ -38,6 +38,7 @@ G_BEGIN_DECLS # include "sysprof-document-mark.h" # include "sysprof-document-metadata.h" # include "sysprof-document-mmap.h" +# include "sysprof-document-overlay.h" # include "sysprof-document-process.h" # include "sysprof-document-sample.h" # include "sysprof-document-traceable.h" diff --git a/src/libsysprof-analyze/sysprof-document-frame.c b/src/libsysprof-analyze/sysprof-document-frame.c index 21e7f1b2..6dbab9f6 100644 --- a/src/libsysprof-analyze/sysprof-document-frame.c +++ b/src/libsysprof-analyze/sysprof-document-frame.c @@ -29,6 +29,7 @@ #include "sysprof-document-log.h" #include "sysprof-document-mark.h" #include "sysprof-document-mmap.h" +#include "sysprof-document-overlay.h" #include "sysprof-document-process.h" #include "sysprof-document-sample.h" @@ -168,6 +169,10 @@ _sysprof_document_frame_new (GMappedFile *mapped_file, gtype = SYSPROF_TYPE_DOCUMENT_FILE_CHUNK; break; + case SYSPROF_CAPTURE_FRAME_OVERLAY: + gtype = SYSPROF_TYPE_DOCUMENT_OVERLAY; + break; + default: gtype = SYSPROF_TYPE_DOCUMENT_FRAME; break; diff --git a/src/libsysprof-analyze/sysprof-document-overlay.c b/src/libsysprof-analyze/sysprof-document-overlay.c new file mode 100644 index 00000000..ac42ffa0 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-document-overlay.c @@ -0,0 +1,142 @@ +/* sysprof-document-overlay.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-document-frame-private.h" +#include "sysprof-document-overlay.h" + +struct _SysprofDocumentOverlay +{ + SysprofDocumentFrame parent_instance; +}; + +struct _SysprofDocumentOverlayClass +{ + SysprofDocumentFrameClass parent_class; +}; + +enum { + PROP_0, + PROP_DESTINATION, + PROP_LAYER, + PROP_SOURCE, + N_PROPS +}; + +G_DEFINE_FINAL_TYPE (SysprofDocumentOverlay, sysprof_document_overlay, SYSPROF_TYPE_DOCUMENT_FRAME) + +static GParamSpec *properties [N_PROPS]; + +static void +sysprof_document_overlay_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofDocumentOverlay *self = SYSPROF_DOCUMENT_OVERLAY (object); + + switch (prop_id) + { + case PROP_DESTINATION: + g_value_set_string (value, sysprof_document_overlay_get_destination (self)); + break; + + case PROP_LAYER: + g_value_set_uint (value, sysprof_document_overlay_get_layer (self)); + break; + + case PROP_SOURCE: + g_value_set_string (value, sysprof_document_overlay_get_source (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_document_overlay_class_init (SysprofDocumentOverlayClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->get_property = sysprof_document_overlay_get_property; + + properties [PROP_LAYER] = + g_param_spec_uint ("layer", NULL, NULL, + 0, G_MAXUINT, 0, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + properties [PROP_DESTINATION] = + g_param_spec_string ("destination", NULL, NULL, + NULL, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + properties [PROP_SOURCE] = + g_param_spec_string ("source", NULL, NULL, + NULL, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); +} + +static void +sysprof_document_overlay_init (SysprofDocumentOverlay *self) +{ +} + +guint +sysprof_document_overlay_get_layer (SysprofDocumentOverlay *self) +{ + const SysprofCaptureOverlay *overlay; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_OVERLAY (self), 0); + + overlay = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureOverlay); + + return overlay->layer; +} + +const char * +sysprof_document_overlay_get_source (SysprofDocumentOverlay *self) +{ + const SysprofCaptureOverlay *overlay; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_OVERLAY (self), 0); + + overlay = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureOverlay); + + return SYSPROF_DOCUMENT_FRAME_CSTRING (self, overlay->data); +} + +const char * +sysprof_document_overlay_get_destination (SysprofDocumentOverlay *self) +{ + const SysprofCaptureOverlay *overlay; + guint16 offset; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_OVERLAY (self), 0); + + overlay = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureOverlay); + + offset = SYSPROF_DOCUMENT_FRAME_UINT16 (self, overlay->src_len); + + return SYSPROF_DOCUMENT_FRAME_CSTRING (self, &overlay->data[offset+1]); +} diff --git a/src/libsysprof-analyze/sysprof-document-overlay.h b/src/libsysprof-analyze/sysprof-document-overlay.h new file mode 100644 index 00000000..86705507 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-document-overlay.h @@ -0,0 +1,47 @@ +/* sysprof-document-overlay.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-document-frame.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_DOCUMENT_OVERLAY (sysprof_document_overlay_get_type()) +#define SYSPROF_IS_DOCUMENT_OVERLAY(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_DOCUMENT_OVERLAY) +#define SYSPROF_DOCUMENT_OVERLAY(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_DOCUMENT_OVERLAY, SysprofDocumentOverlay) +#define SYSPROF_DOCUMENT_OVERLAY_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_DOCUMENT_OVERLAY, SysprofDocumentOverlayClass) + +typedef struct _SysprofDocumentOverlay SysprofDocumentOverlay; +typedef struct _SysprofDocumentOverlayClass SysprofDocumentOverlayClass; + +SYSPROF_AVAILABLE_IN_ALL +GType sysprof_document_overlay_get_type (void) G_GNUC_CONST; +SYSPROF_AVAILABLE_IN_ALL +guint sysprof_document_overlay_get_layer (SysprofDocumentOverlay *self); +SYSPROF_AVAILABLE_IN_ALL +const char *sysprof_document_overlay_get_source (SysprofDocumentOverlay *self); +SYSPROF_AVAILABLE_IN_ALL +const char *sysprof_document_overlay_get_destination (SysprofDocumentOverlay *self); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofDocumentOverlay, g_object_unref) + +G_END_DECLS + diff --git a/src/libsysprof-analyze/tests/test-capture-model.c b/src/libsysprof-analyze/tests/test-capture-model.c index 495f65d5..ae47bf4d 100644 --- a/src/libsysprof-analyze/tests/test-capture-model.c +++ b/src/libsysprof-analyze/tests/test-capture-model.c @@ -70,6 +70,11 @@ main (int argc, else if (SYSPROF_IS_DOCUMENT_FORK (frame)) g_print (" child-pid=%d", sysprof_document_fork_get_child_pid (SYSPROF_DOCUMENT_FORK (frame))); + else if (SYSPROF_IS_DOCUMENT_OVERLAY (frame)) + g_print (" layer=%u source=%s destination=%s", + sysprof_document_overlay_get_layer (SYSPROF_DOCUMENT_OVERLAY (frame)), + sysprof_document_overlay_get_source (SYSPROF_DOCUMENT_OVERLAY (frame)), + sysprof_document_overlay_get_destination (SYSPROF_DOCUMENT_OVERLAY (frame))); else if (SYSPROF_IS_DOCUMENT_MMAP (frame)) g_print (" begin=0x%"G_GINT64_MODIFIER"x end=0x%"G_GINT64_MODIFIER"x offset=0x%"G_GINT64_MODIFIER"x path=%s", sysprof_document_mmap_get_start_address (SYSPROF_DOCUMENT_MMAP (frame)), From 3ff5312c2e205dda600832f68a9e94d524be8dd1 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 19 May 2023 16:57:47 -0700 Subject: [PATCH 0177/1030] libsysprof-analyze: index overlay frames We will want these to process mount namespaces. --- src/libsysprof-analyze/sysprof-document.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index d07ebd7a..6c0c57ae 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -56,6 +56,7 @@ struct _SysprofDocument GtkBitset *traceables; GtkBitset *processes; GtkBitset *mmaps; + GtkBitset *overlays; GtkBitset *pids; GHashTable *files_first_position; @@ -192,11 +193,12 @@ sysprof_document_finalize (GObject *object) g_clear_pointer (&self->mapped_file, g_mapped_file_unref); g_clear_pointer (&self->frames, g_array_unref); - g_clear_pointer (&self->traceables, gtk_bitset_unref); - g_clear_pointer (&self->processes, gtk_bitset_unref); - g_clear_pointer (&self->mmaps, gtk_bitset_unref); g_clear_pointer (&self->file_chunks, gtk_bitset_unref); + g_clear_pointer (&self->mmaps, gtk_bitset_unref); + g_clear_pointer (&self->overlays, gtk_bitset_unref); g_clear_pointer (&self->pids, gtk_bitset_unref); + g_clear_pointer (&self->processes, gtk_bitset_unref); + g_clear_pointer (&self->traceables, gtk_bitset_unref); g_clear_object (&self->mount_namespace); g_clear_object (&self->symbols); @@ -220,11 +222,12 @@ sysprof_document_init (SysprofDocument *self) self->frames = g_array_new (FALSE, FALSE, sizeof (SysprofDocumentFramePointer)); - self->traceables = gtk_bitset_new_empty (); self->file_chunks = gtk_bitset_new_empty (); - self->processes = gtk_bitset_new_empty (); self->mmaps = gtk_bitset_new_empty (); + self->overlays = gtk_bitset_new_empty (); self->pids = gtk_bitset_new_empty (); + self->processes = gtk_bitset_new_empty (); + self->traceables = gtk_bitset_new_empty (); self->files_first_position = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); self->pid_to_process_info = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)sysprof_process_info_unref); @@ -467,6 +470,8 @@ sysprof_document_load_worker (GTask *task, gtk_bitset_add (self->file_chunks, self->frames->len); else if (tainted->type == SYSPROF_CAPTURE_FRAME_MAP) gtk_bitset_add (self->mmaps, self->frames->len); + else if (tainted->type == SYSPROF_CAPTURE_FRAME_OVERLAY) + gtk_bitset_add (self->overlays, self->frames->len); if (tainted->type == SYSPROF_CAPTURE_FRAME_FILE_CHUNK) { From 39e6be47fb6132b78b971e6c701eb6a454006e1d Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 19 May 2023 17:03:35 -0700 Subject: [PATCH 0178/1030] libsysprof-analyze: add plumbing to track overlays This just gets the plumbing into place, we'll still need to use it to do the path translations. --- src/libsysprof-analyze/sysprof-document.c | 27 +++++++++++++++++++ .../sysprof-mount-namespace-private.h | 17 +++++++----- .../sysprof-mount-namespace.c | 10 +++++++ 3 files changed, 47 insertions(+), 7 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index 6c0c57ae..f1f1a80a 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -146,6 +146,9 @@ _sysprof_document_process_info (SysprofDocument *self, g_return_val_if_fail (SYSPROF_IS_DOCUMENT (self), NULL); + if (pid < 0) + return NULL; + process_info = g_hash_table_lookup (self->pid_to_process_info, GINT_TO_POINTER (pid)); if (process_info == NULL && may_create) @@ -394,6 +397,29 @@ sysprof_document_load_mountinfos (SysprofDocument *self) } } +static void +sysprof_document_load_overlays (SysprofDocument *self) +{ + GtkBitsetIter iter; + guint i; + + g_assert (SYSPROF_IS_DOCUMENT (self)); + + if (gtk_bitset_iter_init_first (&iter, self->overlays, &i)) + { + do + { + g_autoptr(SysprofDocumentOverlay) overlay = g_list_model_get_item (G_LIST_MODEL (self), i); + int pid = sysprof_document_frame_get_pid (SYSPROF_DOCUMENT_FRAME (overlay)); + SysprofProcessInfo *process_info = _sysprof_document_process_info (self, pid, TRUE); + + if (process_info != NULL) + sysprof_mount_namespace_add_overlay (process_info->mount_namespace, overlay); + } + while (gtk_bitset_iter_next (&iter, &i)); + } +} + static void sysprof_document_load_worker (GTask *task, gpointer source_object, @@ -492,6 +518,7 @@ sysprof_document_load_worker (GTask *task, sysprof_document_load_mounts (self); sysprof_document_load_mountinfos (self); sysprof_document_load_memory_maps (self); + sysprof_document_load_overlays (self); g_task_return_pointer (task, g_steal_pointer (&self), g_object_unref); } diff --git a/src/libsysprof-analyze/sysprof-mount-namespace-private.h b/src/libsysprof-analyze/sysprof-mount-namespace-private.h index 4f4ecd26..5f0f3d6f 100644 --- a/src/libsysprof-analyze/sysprof-mount-namespace-private.h +++ b/src/libsysprof-analyze/sysprof-mount-namespace-private.h @@ -24,6 +24,7 @@ #include "sysprof-mount-private.h" #include "sysprof-mount-device-private.h" +#include "sysprof-document-overlay.h" G_BEGIN_DECLS @@ -32,12 +33,14 @@ G_BEGIN_DECLS G_DECLARE_FINAL_TYPE (SysprofMountNamespace, sysprof_mount_namespace, SYSPROF, MOUNT_NAMESPACE, GObject) SysprofMountNamespace *sysprof_mount_namespace_new (void); -SysprofMountNamespace *sysprof_mount_namespace_copy (SysprofMountNamespace *self); -void sysprof_mount_namespace_add_device (SysprofMountNamespace *self, - SysprofMountDevice *mount); -void sysprof_mount_namespace_add_mount (SysprofMountNamespace *self, - SysprofMount *mount); -char **sysprof_mount_namespace_translate (SysprofMountNamespace *self, - const char *path); +SysprofMountNamespace *sysprof_mount_namespace_copy (SysprofMountNamespace *self); +void sysprof_mount_namespace_add_device (SysprofMountNamespace *self, + SysprofMountDevice *mount); +void sysprof_mount_namespace_add_mount (SysprofMountNamespace *self, + SysprofMount *mount); +void sysprof_mount_namespace_add_overlay (SysprofMountNamespace *self, + SysprofDocumentOverlay *overlay); +char **sysprof_mount_namespace_translate (SysprofMountNamespace *self, + const char *path); G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-mount-namespace.c b/src/libsysprof-analyze/sysprof-mount-namespace.c index 4fc2cc50..d779b231 100644 --- a/src/libsysprof-analyze/sysprof-mount-namespace.c +++ b/src/libsysprof-analyze/sysprof-mount-namespace.c @@ -147,6 +147,16 @@ sysprof_mount_namespace_add_mount (SysprofMountNamespace *self, g_ptr_array_add (self->mounts, mount); } +void +sysprof_mount_namespace_add_overlay (SysprofMountNamespace *self, + SysprofDocumentOverlay *overlay) +{ + g_return_if_fail (SYSPROF_IS_MOUNT_NAMESPACE (self)); + g_return_if_fail (SYSPROF_IS_DOCUMENT_OVERLAY (overlay)); + + /* TODO */ +} + static SysprofMountDevice * sysprof_mount_namespace_find_device (SysprofMountNamespace *self, SysprofMount *mount, From f1d0d71ee582fa75005b2c2fd242aaf5a5f72aeb Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 19 May 2023 17:07:29 -0700 Subject: [PATCH 0179/1030] libsysprof-analyze: ensure mounts are sorted by length This matches what we did previously so that we find the closest mount match first. --- .../sysprof-mount-namespace.c | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-mount-namespace.c b/src/libsysprof-analyze/sysprof-mount-namespace.c index d779b231..47534bc7 100644 --- a/src/libsysprof-analyze/sysprof-mount-namespace.c +++ b/src/libsysprof-analyze/sysprof-mount-namespace.c @@ -29,6 +29,7 @@ struct _SysprofMountNamespace GObject parent_instance; GPtrArray *devices; GPtrArray *mounts; + guint mounts_dirty : 1; }; static GType @@ -145,6 +146,8 @@ sysprof_mount_namespace_add_mount (SysprofMountNamespace *self, g_return_if_fail (SYSPROF_IS_MOUNT (mount)); g_ptr_array_add (self->mounts, mount); + + self->mounts_dirty = TRUE; } void @@ -193,6 +196,23 @@ sysprof_mount_namespace_find_device (SysprofMountNamespace *self, return NULL; } +static gint +sort_by_length (gconstpointer a, + gconstpointer b) +{ + SysprofMount *mount_a = *(SysprofMount * const *)a; + SysprofMount *mount_b = *(SysprofMount * const *)b; + gsize alen = strlen (sysprof_mount_get_mount_point (mount_a)); + gsize blen = strlen (sysprof_mount_get_mount_point (mount_b)); + + if (alen > blen) + return -1; + else if (blen > alen) + return 1; + else + return 0; +} + /** * sysprof_mount_namespace_translate: * @self: a #SysprofMountNamespace @@ -217,6 +237,12 @@ sysprof_mount_namespace_translate (SysprofMountNamespace *self, g_return_val_if_fail (SYSPROF_IS_MOUNT_NAMESPACE (self), NULL); g_return_val_if_fail (file != NULL, NULL); + if (self->mounts_dirty) + { + g_ptr_array_sort (self->mounts, sort_by_length); + self->mounts_dirty = FALSE; + } + strv = g_array_new (TRUE, FALSE, sizeof (char *)); for (guint i = 0; i < self->mounts->len; i++) From 9d8096baa6df0166c7f11c448064978533b83860 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 19 May 2023 17:30:03 -0700 Subject: [PATCH 0180/1030] libsysprof-analyze: pass-through when there are no matches --- src/libsysprof-analyze/sysprof-mount-namespace.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/libsysprof-analyze/sysprof-mount-namespace.c b/src/libsysprof-analyze/sysprof-mount-namespace.c index 47534bc7..a355c3bc 100644 --- a/src/libsysprof-analyze/sysprof-mount-namespace.c +++ b/src/libsysprof-analyze/sysprof-mount-namespace.c @@ -277,7 +277,14 @@ sysprof_mount_namespace_translate (SysprofMountNamespace *self, } if (strv->len == 0) - return NULL; + { + /* Try to passthrough the path in case we had no matches just to give + * things a chance to decode. This can happen if we never recorded + * the contents of /proc/$$/mountinfo. + */ + char *copy = g_strdup (file); + g_array_append_val (strv, copy); + } return (char **)g_array_free (g_steal_pointer (&strv), FALSE); } From 3df434d950c32ec41344d603a0a53c238c3570da Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Sat, 20 May 2023 10:37:53 -0700 Subject: [PATCH 0181/1030] libsysprof-analyze: resolve symbols in ELF in non-symbol areas If we get an address that doesn't fall within a symbol, we still want to generate a valid string/address range for it here so that we can get nick annotations correct. If we hit the fallback path, we won't get that. --- src/libsysprof-analyze/sysprof-elf.c | 32 ++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-elf.c b/src/libsysprof-analyze/sysprof-elf.c index a5f0a91f..162d7e39 100644 --- a/src/libsysprof-analyze/sysprof-elf.c +++ b/src/libsysprof-analyze/sysprof-elf.c @@ -280,11 +280,12 @@ sysprof_elf_get_debug_link (SysprofElf *self) return elf_parser_get_debug_link (self->parser, &crc32); } -char * -sysprof_elf_get_symbol_at_address (SysprofElf *self, - guint64 address, - guint64 *begin_address, - guint64 *end_address) +static char * +sysprof_elf_get_symbol_at_address_internal (SysprofElf *self, + const char *filename, + guint64 address, + guint64 *begin_address, + guint64 *end_address) { const ElfSym *symbol; char *ret = NULL; @@ -295,7 +296,7 @@ sysprof_elf_get_symbol_at_address (SysprofElf *self, if (self->debug_link_elf != NULL) { - ret = sysprof_elf_get_symbol_at_address (self->debug_link_elf, address, begin_address, end_address); + ret = sysprof_elf_get_symbol_at_address_internal (self->debug_link_elf, filename, address, begin_address, end_address); if (ret != NULL) return ret; @@ -315,6 +316,12 @@ sysprof_elf_get_symbol_at_address (SysprofElf *self, else ret = g_strdup (name); } + else + { + begin = address; + end = address + 1; + ret = g_strdup_printf ("In File %s+0x%"G_GINT64_MODIFIER"x", filename, address); + } if (begin_address) *begin_address = begin; @@ -325,6 +332,19 @@ sysprof_elf_get_symbol_at_address (SysprofElf *self, return ret; } +char * +sysprof_elf_get_symbol_at_address (SysprofElf *self, + guint64 address, + guint64 *begin_address, + guint64 *end_address) +{ + return sysprof_elf_get_symbol_at_address_internal (self, + self->file, + address, + begin_address, + end_address); +} + /** * sysprof_elf_get_debug_link_elf: * @self: a #SysprofElf From 052321db7e2e1e5c1b1565af7b2e15f1916a001d Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Sat, 20 May 2023 17:08:20 -0700 Subject: [PATCH 0182/1030] libsysprof-analyze: add PolicyKit to subsystems --- src/libsysprof-analyze/sysprof-elf.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-elf.c b/src/libsysprof-analyze/sysprof-elf.c index 162d7e39..293b9af2 100644 --- a/src/libsysprof-analyze/sysprof-elf.c +++ b/src/libsysprof-analyze/sysprof-elf.c @@ -86,6 +86,8 @@ static const struct { { "libpangocairo-1.0.so", "Pango" }, { "libpipewire-0.3.so", "Pipewire" }, { "libpixman-1.so", "Pixman" }, + { "libpolkit-agent-1.so", "PolicyKit" }, + { "libpolkit-gobject-1.so", "PolicyKit" }, { "libstdc++.so", "libc" }, { "libwayland-client.so", "Wayland Client" }, { "libwayland-cursor.so", "Wayland Cursor" }, From d057f6992433c8af6b4cbd019e31a53b4f2a6ebb Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Sun, 21 May 2023 12:13:16 -0700 Subject: [PATCH 0183/1030] libsysprof-analyze: cleanup nicks a bit --- src/libsysprof-analyze/sysprof-elf.c | 123 +++++++++++++++++++++------ 1 file changed, 97 insertions(+), 26 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-elf.c b/src/libsysprof-analyze/sysprof-elf.c index 293b9af2..3321827a 100644 --- a/src/libsysprof-analyze/sysprof-elf.c +++ b/src/libsysprof-analyze/sysprof-elf.c @@ -52,47 +52,115 @@ static const struct { const char *library; const char *nick; } nick_table[] = { - { "libEGL.so", "EGL" }, - { "libEGL_mesa.so", "Mesa EGL" }, - { "libGL.so", "GL" }, - { "libX11-xcb.so", "X11" }, - { "libX11.so", "X11" }, { "libc.so", "libc" }, { "libcairo-gobject.so", "Cairo" }, { "libcairo.so", "Cairo" }, { "libclutter-1.0.so", "Clutter" }, { "libclutter-glx-1.0.so", "Clutter" }, { "libffi.so", "libffi" }, - { "libgdk-3.so", "GDK 3" }, - { "libgio-2.0.so", "Gio" }, - { "libgirepository-1.0.so", "Introspection" }, { "libgjs.so", "GJS" }, + { "libgstreamer-1-0.so", "GStreamer" }, + { "libgudev-1.0.so", "udev" }, + { "libibus-1.0.so", "IBus" }, + { "libinput.so", "Mutter" }, + { "ld-linux-x86-64.so", "glibc" }, + { "libnss_sss.so", "NSS" }, + { "libsss_debug.so", "SSSD" }, + { "libsss_util.so", "SSSD" }, + { "libnss_systemd.so", "NSS" }, + { "libpcre2-8.so", "PCRE" }, + { "libpixman-1.so", "Pixman" }, + { "libpolkit-agent-1.so", "PolicyKit" }, + { "libpolkit-gobject-1.so", "PolicyKit" }, + { "libselinux.so", "SELinux" }, + { "libssl3.so", "NSS" }, + { "libstdc++.so", "libc" }, + { "libsystemd.so", "systemd" }, + { "libudev.so", "udev" }, + { "libvte-2.91.so", "VTE" }, + { "libvte-2.91-gtk4.so", "VTE" }, + { "libxul.so", "XUL" }, + { "libz.so", "Zlib" }, + { "libzstd.so", "Zstd" }, + + /* GObject */ { "libglib-2.0.so", "GLib" }, { "libgobject-2.0.so", "GObject" }, - { "libgstreamer-1-0.so", "GStreamer" }, - { "libgtk-3.so", "GTK 3" }, - { "libgtk-4.so", "GTK 4" }, - { "libgtksourceview-3.0.so", "GtkSourceView 3" }, - { "libgtksourceview-4.so", "GtkSourceView 4" }, - { "libgtksourceview-5.so", "GtkSourceView 5" }, + { "libgio-2.0.so", "Gio" }, + { "libgirepository-1.0.so", "Introspection" }, + + /* Pango and Harfbuzz */ + { "libfribidi.so", "Fribidi" }, + { "libpango-1.0.so", "Pango" }, + { "libpangocairo-1.0.so", "Pango" }, + { "libpangoft2-1.0.so", "Pango" }, { "libharfbuzz-cairo.so", "Harfbuzz" }, { "libharfbuzz-gobject.so", "Harfbuzz" }, { "libharfbuzz-icu.so", "Harfbuzz" }, { "libharfbuzz-subset.so", "Harfbuzz" }, { "libharfbuzz.so", "Harfbuzz" }, - { "libinput.so", "Mutter" }, - { "libmutter-12.so", "Mutter" }, - { "libpango-1.0.so", "Pango" }, - { "libpangocairo-1.0.so", "Pango" }, - { "libpipewire-0.3.so", "Pipewire" }, - { "libpixman-1.so", "Pixman" }, - { "libpolkit-agent-1.so", "PolicyKit" }, - { "libpolkit-gobject-1.so", "PolicyKit" }, - { "libstdc++.so", "libc" }, + + /* GTK */ + { "libgtk-3.so", "GTK 3" }, + { "libgdk-3.so", "GTK 3" }, + { "libgtk-4.so", "GTK 4" }, + { "libgraphene-1.0.so", "Graphene" }, + + /* Xorg/X11 */ + { "libX11-xcb.so", "X11" }, + { "libX11.so", "X11" }, + { "libxcb.so", "X11" }, + { "libxkbcommon.so", "XKB" }, + + /* Wayland */ { "libwayland-client.so", "Wayland Client" }, { "libwayland-cursor.so", "Wayland Cursor" }, { "libwayland-egl.so", "Wayland EGL" }, { "libwayland-server.so", "Wayland Server" }, + + /* Mutter/Clutter/Shell */ + { "libmutter-12.so", "Mutter" }, + { "libmutter-cogl-12.so", "Mutter" }, + { "libmutter-clutter-12.so", "Mutter" }, + { "libst-12.so", "GNOME Shell" }, + + /* GtkSourceView */ + { "libgtksourceview-3.0.so", "GtkSourceView" }, + { "libgtksourceview-4.so", "GtkSourceView" }, + { "libgtksourceview-5.so", "GtkSourceView" }, + + /* Pipewire and Legacy Audio modules */ + { "libasound.so", "ALSA" }, + { "libpipewire-0.3.so", "Pipewire" }, + { "libpipewire-module-client-node.so", "Pipewire" }, + { "libpipewire-module-protocol-pulse.so", "Pipewire" }, + { "libpulse.so", "PulseAudio" }, + { "libpulsecommon-16.1.so", "PulseAudio" }, + { "libspa-alsa.so", "Pipewire" }, + { "libspa-audioconvert.so", "Pipewire" }, + { "libspa-support.so", "Pipewire" }, + + /* OpenGL base libraries */ + { "libEGL.so", "EGL" }, + { "libGL.so", "GL" }, + + /* Mesa and DRI Drivers */ + { "crocus_dri.so", "Mesa" }, + { "i915_dri.so", "Mesa" }, + { "i965_drv_video.so", "Mesa" }, + { "iHD_drv_video.so", "Mesa" }, + { "iris_dri.so", "Mesa" }, + { "kms_swrast_dri.so", "Mesa" }, + { "libEGL_mesa.so", "Mesa" }, + { "libdrm.so", "Mesa" }, + { "nouveau_dri.so", "Mesa" }, + { "r300_dri.so", "Mesa" }, + { "r600_dri.so", "Mesa" }, + { "radeonsi_dri.so", "Mesa" }, + { "swrast_dri.so", "Mesa" }, + { "virtio_gpu_dri.so", "Mesa" }, + { "vmwgfx_dri.so", "Mesa" }, + { "zink_dri.so", "Mesa" }, }; static void @@ -191,9 +259,12 @@ sysprof_elf_class_init (SysprofElfClass *klass) nicks = g_hash_table_new (g_str_hash, g_str_equal); for (guint i = 0; i < G_N_ELEMENTS (nick_table); i++) - g_hash_table_insert (nicks, - (char *)nick_table[i].library, - (char *)nick_table[i].nick); + { + g_assert (g_str_has_suffix (nick_table[i].library, ".so")); + g_hash_table_insert (nicks, + (char *)nick_table[i].library, + (char *)nick_table[i].nick); + } } static void From c7f025d4d394c2738d8d7a607564be960912ad22 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 22 May 2023 10:59:22 -0700 Subject: [PATCH 0184/1030] libsysprof-analyze: allow filtering by pid --- src/libsysprof-analyze/tests/test-list-processes.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/libsysprof-analyze/tests/test-list-processes.c b/src/libsysprof-analyze/tests/test-list-processes.c index 5b4fffcf..506260de 100644 --- a/src/libsysprof-analyze/tests/test-list-processes.c +++ b/src/libsysprof-analyze/tests/test-list-processes.c @@ -31,13 +31,17 @@ main (int argc, g_autoptr(GListModel) processes = NULL; g_autoptr(GError) error = NULL; guint n_items; + int pid = -1; if (argc < 2) { - g_printerr ("usage: %s CAPTURE_FILE\n", argv[0]); + g_printerr ("usage: %s CAPTURE_FILE [PID]\n", argv[0]); return 1; } + if (argc == 3) + pid = atoi (argv[2]); + loader = sysprof_document_loader_new (argv[1]); sysprof_document_loader_set_symbolizer (loader, sysprof_no_symbolizer_get ()); @@ -58,6 +62,9 @@ main (int argc, guint n_maps; guint n_mounts; + if (pid != -1 && pid != sysprof_document_frame_get_pid (SYSPROF_DOCUMENT_FRAME (process))) + continue; + g_print ("%d: %s\n", sysprof_document_frame_get_pid (SYSPROF_DOCUMENT_FRAME (process)), sysprof_document_process_get_command_line (process)); From d28080b9adb759b04602d2dc036affd809b40fd3 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 22 May 2023 12:18:57 -0700 Subject: [PATCH 0185/1030] libsysprof-analyze: add SysprofDocumentJitmap This adds a specific frame type for the Jitmap frames in the capture files. You can iterate them without having to bswap as well, which is why this does not use the SysprofCaptureJitmapIter (which does require bswap'd frames). --- src/libsysprof-analyze/meson.build | 2 + src/libsysprof-analyze/sysprof-analyze.h | 1 + .../sysprof-document-frame-private.h | 3 + .../sysprof-document-frame.c | 5 + .../sysprof-document-jitmap.c | 166 ++++++++++++++++++ .../sysprof-document-jitmap.h | 49 ++++++ src/libsysprof-analyze/tests/meson.build | 1 + .../tests/test-capture-model.c | 3 + .../tests/test-list-jitmap.c | 74 ++++++++ 9 files changed, 304 insertions(+) create mode 100644 src/libsysprof-analyze/sysprof-document-jitmap.c create mode 100644 src/libsysprof-analyze/sysprof-document-jitmap.h create mode 100644 src/libsysprof-analyze/tests/test-list-jitmap.c diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build index 3b5f72c6..30160e5f 100644 --- a/src/libsysprof-analyze/meson.build +++ b/src/libsysprof-analyze/meson.build @@ -7,6 +7,7 @@ libsysprof_analyze_public_sources = [ 'sysprof-document-file-chunk.c', 'sysprof-document-fork.c', 'sysprof-document-frame.c', + 'sysprof-document-jitmap.c', 'sysprof-document-loader.c', 'sysprof-document-log.c', 'sysprof-document-mark.c', @@ -51,6 +52,7 @@ libsysprof_analyze_public_headers = [ 'sysprof-document-file-chunk.h', 'sysprof-document-fork.h', 'sysprof-document-frame.h', + 'sysprof-document-jitmap.h', 'sysprof-document-loader.h', 'sysprof-document-log.h', 'sysprof-document-mark.h', diff --git a/src/libsysprof-analyze/sysprof-analyze.h b/src/libsysprof-analyze/sysprof-analyze.h index 3e2b186d..68c32272 100644 --- a/src/libsysprof-analyze/sysprof-analyze.h +++ b/src/libsysprof-analyze/sysprof-analyze.h @@ -34,6 +34,7 @@ G_BEGIN_DECLS # include "sysprof-document-fork.h" # include "sysprof-document-frame.h" # include "sysprof-document-loader.h" +# include "sysprof-document-jitmap.h" # include "sysprof-document-log.h" # include "sysprof-document-mark.h" # include "sysprof-document-metadata.h" diff --git a/src/libsysprof-analyze/sysprof-document-frame-private.h b/src/libsysprof-analyze/sysprof-document-frame-private.h index 91a74a8a..50326fe9 100644 --- a/src/libsysprof-analyze/sysprof-document-frame-private.h +++ b/src/libsysprof-analyze/sysprof-document-frame-private.h @@ -46,6 +46,9 @@ SysprofDocumentFrame *_sysprof_document_frame_new (GMappedFile *ma guint16 frame_len, gboolean needs_swap); +#define SYSPROF_DOCUMENT_FRAME_ENDPTR(obj) \ + (&((const guint8 *)SYSPROF_DOCUMENT_FRAME(obj)->frame)[SYSPROF_DOCUMENT_FRAME(obj)->frame_len]) + #define SYSPROF_DOCUMENT_FRAME_GET(obj, type) \ ((const type *)(SYSPROF_DOCUMENT_FRAME(obj)->frame)) diff --git a/src/libsysprof-analyze/sysprof-document-frame.c b/src/libsysprof-analyze/sysprof-document-frame.c index 6dbab9f6..09664eab 100644 --- a/src/libsysprof-analyze/sysprof-document-frame.c +++ b/src/libsysprof-analyze/sysprof-document-frame.c @@ -27,6 +27,7 @@ #include "sysprof-document-file-chunk.h" #include "sysprof-document-fork.h" #include "sysprof-document-log.h" +#include "sysprof-document-jitmap.h" #include "sysprof-document-mark.h" #include "sysprof-document-mmap.h" #include "sysprof-document-overlay.h" @@ -173,6 +174,10 @@ _sysprof_document_frame_new (GMappedFile *mapped_file, gtype = SYSPROF_TYPE_DOCUMENT_OVERLAY; break; + case SYSPROF_CAPTURE_FRAME_JITMAP: + gtype = SYSPROF_TYPE_DOCUMENT_JITMAP; + break; + default: gtype = SYSPROF_TYPE_DOCUMENT_FRAME; break; diff --git a/src/libsysprof-analyze/sysprof-document-jitmap.c b/src/libsysprof-analyze/sysprof-document-jitmap.c new file mode 100644 index 00000000..d4ac04f6 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-document-jitmap.c @@ -0,0 +1,166 @@ +/* sysprof-document-jitmap.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-document-frame-private.h" +#include "sysprof-document-jitmap.h" + +typedef struct _Jitmap +{ + SysprofAddress address; + const char *name; +} Jitmap; + +struct _SysprofDocumentJitmap +{ + SysprofDocumentFrame parent_instance; + GArray *jitmaps; + guint initialized : 1; +}; + +struct _SysprofDocumentJitmapClass +{ + SysprofDocumentFrameClass parent_class; +}; + +enum { + PROP_0, + PROP_SIZE, + N_PROPS +}; + +G_DEFINE_FINAL_TYPE (SysprofDocumentJitmap, sysprof_document_jitmap, SYSPROF_TYPE_DOCUMENT_FRAME) + +static GParamSpec *properties [N_PROPS]; + +static void +sysprof_document_jitmap_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofDocumentJitmap *self = SYSPROF_DOCUMENT_JITMAP (object); + + switch (prop_id) + { + case PROP_SIZE: + g_value_set_uint (value, sysprof_document_jitmap_get_size (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_document_jitmap_class_init (SysprofDocumentJitmapClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->get_property = sysprof_document_jitmap_get_property; + + properties [PROP_SIZE] = + g_param_spec_string ("size", NULL, NULL, + NULL, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); +} + +static void +sysprof_document_jitmap_init (SysprofDocumentJitmap *self) +{ + self->jitmaps = g_array_new (FALSE, FALSE, sizeof (Jitmap)); +} + +guint +sysprof_document_jitmap_get_size (SysprofDocumentJitmap *self) +{ + const SysprofCaptureJitmap *jitmap; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_JITMAP (self), 0); + + jitmap = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureJitmap); + + return SYSPROF_DOCUMENT_FRAME_UINT32 (self, jitmap->n_jitmaps); +} + +/** + * sysprof_document_jitmap_get_mapping: + * @self: a #SysprofDocumentJitmap + * @nth: the index of the mapping + * @address: (out): a location for the address + * + * Gets the @nth mapping and returns it as @address and the return value + * of this function. + * + * Returns: (nullable): the name of the symbol, or %NULL if @nth is + * out of bounds. + */ +const char * +sysprof_document_jitmap_get_mapping (SysprofDocumentJitmap *self, + guint nth, + SysprofAddress *address) +{ + const SysprofCaptureJitmap *frame; + const Jitmap *j; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_JITMAP (self), NULL); + g_return_val_if_fail (address != NULL, NULL); + + if G_UNLIKELY (!self->initialized) + { + const guint8 *pos; + const guint8 *endptr; + + self->initialized = TRUE; + + frame = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureJitmap); + endptr = SYSPROF_DOCUMENT_FRAME_ENDPTR (self); + pos = frame->data; + + while (pos < endptr) + { + Jitmap map; + + if (pos + sizeof map.address >= endptr) + break; + + memcpy (&map.address, pos, sizeof map.address); + pos += sizeof map.address; + + if (!(map.name = SYSPROF_DOCUMENT_FRAME_CSTRING (self, (const char *)pos))) + break; + + pos += strlen (map.name) + 1; + + g_array_append_val (self->jitmaps, map); + } + } + + if (nth >= self->jitmaps->len) + return NULL; + + j = &g_array_index (self->jitmaps, Jitmap, nth); + *address = j->address; + return j->name; +} + diff --git a/src/libsysprof-analyze/sysprof-document-jitmap.h b/src/libsysprof-analyze/sysprof-document-jitmap.h new file mode 100644 index 00000000..f6b83543 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-document-jitmap.h @@ -0,0 +1,49 @@ +/* sysprof-document-jitmap.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +#include "sysprof-document-frame.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_DOCUMENT_JITMAP (sysprof_document_jitmap_get_type()) +#define SYSPROF_IS_DOCUMENT_JITMAP(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_DOCUMENT_JITMAP) +#define SYSPROF_DOCUMENT_JITMAP(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_DOCUMENT_JITMAP, SysprofDocumentJitmap) +#define SYSPROF_DOCUMENT_JITMAP_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_DOCUMENT_JITMAP, SysprofDocumentJitmapClass) + +typedef struct _SysprofDocumentJitmap SysprofDocumentJitmap; +typedef struct _SysprofDocumentJitmapClass SysprofDocumentJitmapClass; + +SYSPROF_AVAILABLE_IN_ALL +GType sysprof_document_jitmap_get_type (void) G_GNUC_CONST; +SYSPROF_AVAILABLE_IN_ALL +guint sysprof_document_jitmap_get_size (SysprofDocumentJitmap *self); +SYSPROF_AVAILABLE_IN_ALL +const char *sysprof_document_jitmap_get_mapping (SysprofDocumentJitmap *self, + guint position, + SysprofAddress *address); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofDocumentJitmap, g_object_unref) + +G_END_DECLS + diff --git a/src/libsysprof-analyze/tests/meson.build b/src/libsysprof-analyze/tests/meson.build index c57a231a..5220419c 100644 --- a/src/libsysprof-analyze/tests/meson.build +++ b/src/libsysprof-analyze/tests/meson.build @@ -15,6 +15,7 @@ libsysprof_analyze_testsuite = { 'test-capture-model' : {'skip': true}, 'test-elf-loader' : {'skip': true}, 'test-list-files' : {'skip': true}, + 'test-list-jitmap' : {'skip': true}, 'test-print-file' : {'skip': true}, 'test-list-processes' : {'skip': true}, 'test-list-address-layout' : {'skip': true}, diff --git a/src/libsysprof-analyze/tests/test-capture-model.c b/src/libsysprof-analyze/tests/test-capture-model.c index ae47bf4d..aa5dd0f9 100644 --- a/src/libsysprof-analyze/tests/test-capture-model.c +++ b/src/libsysprof-analyze/tests/test-capture-model.c @@ -81,6 +81,9 @@ main (int argc, sysprof_document_mmap_get_end_address (SYSPROF_DOCUMENT_MMAP (frame)), sysprof_document_mmap_get_file_offset (SYSPROF_DOCUMENT_MMAP (frame)), sysprof_document_mmap_get_file (SYSPROF_DOCUMENT_MMAP (frame))); + else if (SYSPROF_IS_DOCUMENT_JITMAP (frame)) + g_print (" n_jitmaps=%u", + sysprof_document_jitmap_get_size (SYSPROF_DOCUMENT_JITMAP (frame))); else if (SYSPROF_IS_DOCUMENT_ALLOCATION (frame)) { if (sysprof_document_allocation_is_free (SYSPROF_DOCUMENT_ALLOCATION (frame))) diff --git a/src/libsysprof-analyze/tests/test-list-jitmap.c b/src/libsysprof-analyze/tests/test-list-jitmap.c new file mode 100644 index 00000000..ea1703b1 --- /dev/null +++ b/src/libsysprof-analyze/tests/test-list-jitmap.c @@ -0,0 +1,74 @@ +/* test-list-jitmap.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include + +#include "sysprof-document-private.h" + +int +main (int argc, + char *argv[]) +{ + g_autoptr(SysprofDocumentLoader) loader = NULL; + g_autoptr(SysprofDocument) document = NULL; + g_autoptr(GError) error = NULL; + guint n_items; + + if (argc < 2) + { + g_printerr ("usage: %s CAPTURE_FILE\n", argv[0]); + return 1; + } + + loader = sysprof_document_loader_new (argv[1]); + sysprof_document_loader_set_symbolizer (loader, sysprof_no_symbolizer_get ()); + + if (!(document = sysprof_document_loader_load (loader, NULL, &error))) + { + g_printerr ("Failed to open capture: %s\n", error->message); + return 1; + } + + n_items = g_list_model_get_n_items (G_LIST_MODEL (document)); + + for (guint i = 0; i < n_items; i++) + { + g_autoptr(SysprofDocumentFrame) frame = g_list_model_get_item ((GListModel *)document, i); + + if (SYSPROF_IS_DOCUMENT_JITMAP (frame)) + { + SysprofDocumentJitmap *jitmap = SYSPROF_DOCUMENT_JITMAP (frame); + guint size = sysprof_document_jitmap_get_size (jitmap); + + for (guint j = 0; j < size; j++) + { + SysprofAddress address; + const char *name; + + if (!(name = sysprof_document_jitmap_get_mapping (jitmap, j, &address))) + break; + + g_print ("0x%"G_GINT64_MODIFIER"x: %s\n", address, name); + } + } + } + + return 0; +} From 1e6fc6e668bd71cb1b8243ded9b50b5358d4805a Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 22 May 2023 12:19:07 -0700 Subject: [PATCH 0186/1030] libsysprof-analyze: sort headers --- src/libsysprof-analyze/meson.build | 2 +- src/libsysprof-analyze/sysprof-analyze.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build index 30160e5f..8883f59f 100644 --- a/src/libsysprof-analyze/meson.build +++ b/src/libsysprof-analyze/meson.build @@ -63,8 +63,8 @@ libsysprof_analyze_public_headers = [ 'sysprof-document-sample.h', 'sysprof-document-traceable.h', 'sysprof-elf-symbolizer.h', - 'sysprof-mount.h', 'sysprof-kallsyms-symbolizer.h', + 'sysprof-mount.h', 'sysprof-multi-symbolizer.h', 'sysprof-no-symbolizer.h', 'sysprof-symbol.h', diff --git a/src/libsysprof-analyze/sysprof-analyze.h b/src/libsysprof-analyze/sysprof-analyze.h index 68c32272..771ed55c 100644 --- a/src/libsysprof-analyze/sysprof-analyze.h +++ b/src/libsysprof-analyze/sysprof-analyze.h @@ -44,8 +44,8 @@ G_BEGIN_DECLS # include "sysprof-document-sample.h" # include "sysprof-document-traceable.h" # include "sysprof-elf-symbolizer.h" -# include "sysprof-mount.h" # include "sysprof-kallsyms-symbolizer.h" +# include "sysprof-mount.h" # include "sysprof-multi-symbolizer.h" # include "sysprof-no-symbolizer.h" # include "sysprof-symbol.h" From 77cf99850f9871418b0bd967729bf7328c9bef8b Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 22 May 2023 12:21:51 -0700 Subject: [PATCH 0187/1030] libsysprof-analyze: use switch/jumptable --- src/libsysprof-analyze/sysprof-document.c | 37 ++++++++++++++++------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index f1f1a80a..70a09f6c 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -487,17 +487,32 @@ sysprof_document_load_worker (GTask *task, gtk_bitset_add (self->pids, pid); - if (tainted->type == SYSPROF_CAPTURE_FRAME_SAMPLE || - tainted->type == SYSPROF_CAPTURE_FRAME_ALLOCATION) - gtk_bitset_add (self->traceables, self->frames->len); - else if (tainted->type == SYSPROF_CAPTURE_FRAME_PROCESS) - gtk_bitset_add (self->processes, self->frames->len); - else if (tainted->type == SYSPROF_CAPTURE_FRAME_FILE_CHUNK) - gtk_bitset_add (self->file_chunks, self->frames->len); - else if (tainted->type == SYSPROF_CAPTURE_FRAME_MAP) - gtk_bitset_add (self->mmaps, self->frames->len); - else if (tainted->type == SYSPROF_CAPTURE_FRAME_OVERLAY) - gtk_bitset_add (self->overlays, self->frames->len); + switch ((int)tainted->type) + { + case SYSPROF_CAPTURE_FRAME_ALLOCATION: + case SYSPROF_CAPTURE_FRAME_SAMPLE: + gtk_bitset_add (self->traceables, self->frames->len); + break; + + case SYSPROF_CAPTURE_FRAME_PROCESS: + gtk_bitset_add (self->processes, self->frames->len); + break; + + case SYSPROF_CAPTURE_FRAME_FILE_CHUNK: + gtk_bitset_add (self->file_chunks, self->frames->len); + break; + + case SYSPROF_CAPTURE_FRAME_MAP: + gtk_bitset_add (self->mmaps, self->frames->len); + break; + + case SYSPROF_CAPTURE_FRAME_OVERLAY: + gtk_bitset_add (self->overlays, self->frames->len); + break; + + default: + break; + } if (tainted->type == SYSPROF_CAPTURE_FRAME_FILE_CHUNK) { From 8abc55f0e60da6bd20a182767bf5164b87924318 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 22 May 2023 12:23:45 -0700 Subject: [PATCH 0188/1030] libsysprof-analyze: include element type in return comment --- src/libsysprof-analyze/sysprof-document.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index 70a09f6c..14ee2a84 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -742,7 +742,7 @@ sysprof_document_lookup_file (SysprofDocument *self, * * Gets a #GListModel of #SysprofDocumentFile * - * Returns: (transfer full): a #GListModel + * Returns: (transfer full): a #GListModel of #SysprofDocumentFile */ GListModel * sysprof_document_list_files (SysprofDocument *self) @@ -799,7 +799,7 @@ sysprof_document_list_files (SysprofDocument *self) * Gets a #GListModel containing #SysprofDocumentTraceable found within * the #SysprofDocument. * - * Returns: (transfer full): a #GListModel + * Returns: (transfer full): a #GListModel of #SysprofTraceable */ GListModel * sysprof_document_list_traceables (SysprofDocument *self) @@ -816,7 +816,7 @@ sysprof_document_list_traceables (SysprofDocument *self) * Gets a #GListModel containing #SysprofDocumentProcess found within * the #SysprofDocument. * - * Returns: (transfer full): a #GListModel + * Returns: (transfer full): a #GListModel of #SysprofDocumentProcess */ GListModel * sysprof_document_list_processes (SysprofDocument *self) From aab4408f73d761ba67c82c57fd12612947adac51 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 22 May 2023 12:25:55 -0700 Subject: [PATCH 0189/1030] libsysprof-analyze: index jitmaps for faster access We want quick access to these for the jitmap symbolizer, so index them like we do other important types with GtkBitset to get their frame positions. --- src/libsysprof-analyze/sysprof-document.c | 25 +++++++++++++++++++ src/libsysprof-analyze/sysprof-document.h | 2 ++ .../tests/test-list-jitmap.c | 6 +++-- 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index 14ee2a84..98656267 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -30,6 +30,7 @@ #include "sysprof-document-file-chunk.h" #include "sysprof-document-file-private.h" #include "sysprof-document-frame-private.h" +#include "sysprof-document-jitmap.h" #include "sysprof-document-mmap.h" #include "sysprof-document-process-private.h" #include "sysprof-document-symbols-private.h" @@ -58,6 +59,7 @@ struct _SysprofDocument GtkBitset *mmaps; GtkBitset *overlays; GtkBitset *pids; + GtkBitset *jitmaps; GHashTable *files_first_position; GHashTable *pid_to_process_info; @@ -197,6 +199,7 @@ sysprof_document_finalize (GObject *object) g_clear_pointer (&self->frames, g_array_unref); g_clear_pointer (&self->file_chunks, gtk_bitset_unref); + g_clear_pointer (&self->jitmaps, gtk_bitset_unref); g_clear_pointer (&self->mmaps, gtk_bitset_unref); g_clear_pointer (&self->overlays, gtk_bitset_unref); g_clear_pointer (&self->pids, gtk_bitset_unref); @@ -226,6 +229,7 @@ sysprof_document_init (SysprofDocument *self) self->frames = g_array_new (FALSE, FALSE, sizeof (SysprofDocumentFramePointer)); self->file_chunks = gtk_bitset_new_empty (); + self->jitmaps = gtk_bitset_new_empty (); self->mmaps = gtk_bitset_new_empty (); self->overlays = gtk_bitset_new_empty (); self->pids = gtk_bitset_new_empty (); @@ -502,6 +506,10 @@ sysprof_document_load_worker (GTask *task, gtk_bitset_add (self->file_chunks, self->frames->len); break; + case SYSPROF_CAPTURE_FRAME_JITMAP: + gtk_bitset_add (self->jitmaps, self->frames->len); + break; + case SYSPROF_CAPTURE_FRAME_MAP: gtk_bitset_add (self->mmaps, self->frames->len); break; @@ -826,6 +834,23 @@ sysprof_document_list_processes (SysprofDocument *self) return _sysprof_document_bitset_index_new (G_LIST_MODEL (self), self->processes); } +/** + * sysprof_document_list_jitmaps: + * @self: a #SysprofDocument + * + * Gets a #GListModel containing #SysprofDocumentJitmap found within + * the #SysprofDocument. + * + * Returns: (transfer full): a #GListModel of #SysprofDocumentJitmap + */ +GListModel * +sysprof_document_list_jitmaps (SysprofDocument *self) +{ + g_return_val_if_fail (SYSPROF_IS_DOCUMENT (self), NULL); + + return _sysprof_document_bitset_index_new (G_LIST_MODEL (self), self->jitmaps); +} + /** * sysprof_document_symbolize_traceable: (skip) * @self: a #SysprofDocument diff --git a/src/libsysprof-analyze/sysprof-document.h b/src/libsysprof-analyze/sysprof-document.h index 19567d2d..465a203f 100644 --- a/src/libsysprof-analyze/sysprof-document.h +++ b/src/libsysprof-analyze/sysprof-document.h @@ -45,6 +45,8 @@ GListModel *sysprof_document_list_traceables (SysprofDocument SYSPROF_AVAILABLE_IN_ALL GListModel *sysprof_document_list_processes (SysprofDocument *self); SYSPROF_AVAILABLE_IN_ALL +GListModel *sysprof_document_list_jitmaps (SysprofDocument *self); +SYSPROF_AVAILABLE_IN_ALL guint sysprof_document_symbolize_traceable (SysprofDocument *self, SysprofDocumentTraceable *traceable, SysprofSymbol **symbols, diff --git a/src/libsysprof-analyze/tests/test-list-jitmap.c b/src/libsysprof-analyze/tests/test-list-jitmap.c index ea1703b1..1029ff52 100644 --- a/src/libsysprof-analyze/tests/test-list-jitmap.c +++ b/src/libsysprof-analyze/tests/test-list-jitmap.c @@ -28,6 +28,7 @@ main (int argc, { g_autoptr(SysprofDocumentLoader) loader = NULL; g_autoptr(SysprofDocument) document = NULL; + g_autoptr(GListModel) model = NULL; g_autoptr(GError) error = NULL; guint n_items; @@ -46,11 +47,12 @@ main (int argc, return 1; } - n_items = g_list_model_get_n_items (G_LIST_MODEL (document)); + model = sysprof_document_list_jitmaps (document); + n_items = g_list_model_get_n_items (model); for (guint i = 0; i < n_items; i++) { - g_autoptr(SysprofDocumentFrame) frame = g_list_model_get_item ((GListModel *)document, i); + g_autoptr(SysprofDocumentFrame) frame = g_list_model_get_item (model, i); if (SYSPROF_IS_DOCUMENT_JITMAP (frame)) { From a1f83c8104b8955c33fe15c9c1c4a63ef684fe57 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 22 May 2023 13:19:50 -0700 Subject: [PATCH 0190/1030] libsysprof-analyze: translate address to native endianness --- src/libsysprof-analyze/sysprof-document-jitmap.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-document-jitmap.c b/src/libsysprof-analyze/sysprof-document-jitmap.c index d4ac04f6..aa4f8e05 100644 --- a/src/libsysprof-analyze/sysprof-document-jitmap.c +++ b/src/libsysprof-analyze/sysprof-document-jitmap.c @@ -139,13 +139,16 @@ sysprof_document_jitmap_get_mapping (SysprofDocumentJitmap *self, while (pos < endptr) { + SysprofAddress addr; Jitmap map; - if (pos + sizeof map.address >= endptr) + if (pos + sizeof addr >= endptr) break; - memcpy (&map.address, pos, sizeof map.address); - pos += sizeof map.address; + memcpy (&addr, pos, sizeof addr); + pos += sizeof addr; + + map.address = SYSPROF_DOCUMENT_FRAME_UINT64 (self, addr); if (!(map.name = SYSPROF_DOCUMENT_FRAME_CSTRING (self, (const char *)pos))) break; From b641e41592c8fbdca85495e99218ba45a4addaa2 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 22 May 2023 13:20:08 -0700 Subject: [PATCH 0191/1030] libsysprof-analyze: include elf loader by default --- src/libsysprof-analyze/sysprof-document-loader.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-document-loader.c b/src/libsysprof-analyze/sysprof-document-loader.c index 0c6e5d00..44ba8669 100644 --- a/src/libsysprof-analyze/sysprof-document-loader.c +++ b/src/libsysprof-analyze/sysprof-document-loader.c @@ -26,6 +26,7 @@ #include "sysprof-bundled-symbolizer.h" #include "sysprof-document-loader.h" #include "sysprof-document-private.h" +#include "sysprof-elf-symbolizer.h" #include "sysprof-kallsyms-symbolizer.h" #include "sysprof-multi-symbolizer.h" @@ -148,6 +149,7 @@ set_default_symbolizer (SysprofDocumentLoader *self) multi = sysprof_multi_symbolizer_new (); sysprof_multi_symbolizer_take (multi, sysprof_bundled_symbolizer_new ()); sysprof_multi_symbolizer_take (multi, sysprof_kallsyms_symbolizer_new ()); + sysprof_multi_symbolizer_take (multi, sysprof_elf_symbolizer_new ()); self->symbolizer = SYSPROF_SYMBOLIZER (g_steal_pointer (&multi)); } From 9e5a241feff18f5b4658e006d39adaa7e93c07f7 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 22 May 2023 13:21:06 -0700 Subject: [PATCH 0192/1030] libsysprof-analyze: add jitmap symbolizer This symbolizes using the SysprofCaptureJitmap frames within the capture document. Currently it only implements the fast path which can avoid a binary search on the jitmap data. --- src/libsysprof-analyze/meson.build | 2 + src/libsysprof-analyze/sysprof-analyze.h | 1 + .../sysprof-document-loader.c | 2 + .../sysprof-jitmap-symbolizer.c | 238 ++++++++++++++++++ .../sysprof-jitmap-symbolizer.h | 42 ++++ src/libsysprof-analyze/tests/test-symbolize.c | 3 +- 6 files changed, 287 insertions(+), 1 deletion(-) create mode 100644 src/libsysprof-analyze/sysprof-jitmap-symbolizer.c create mode 100644 src/libsysprof-analyze/sysprof-jitmap-symbolizer.h diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build index 8883f59f..c586f2bd 100644 --- a/src/libsysprof-analyze/meson.build +++ b/src/libsysprof-analyze/meson.build @@ -18,6 +18,7 @@ libsysprof_analyze_public_sources = [ 'sysprof-document-sample.c', 'sysprof-document-traceable.c', 'sysprof-elf-symbolizer.c', + 'sysprof-jitmap-symbolizer.c', 'sysprof-kallsyms-symbolizer.c', 'sysprof-multi-symbolizer.c', 'sysprof-no-symbolizer.c', @@ -63,6 +64,7 @@ libsysprof_analyze_public_headers = [ 'sysprof-document-sample.h', 'sysprof-document-traceable.h', 'sysprof-elf-symbolizer.h', + 'sysprof-jitmap-symbolizer.h', 'sysprof-kallsyms-symbolizer.h', 'sysprof-mount.h', 'sysprof-multi-symbolizer.h', diff --git a/src/libsysprof-analyze/sysprof-analyze.h b/src/libsysprof-analyze/sysprof-analyze.h index 771ed55c..f2bf627b 100644 --- a/src/libsysprof-analyze/sysprof-analyze.h +++ b/src/libsysprof-analyze/sysprof-analyze.h @@ -44,6 +44,7 @@ G_BEGIN_DECLS # include "sysprof-document-sample.h" # include "sysprof-document-traceable.h" # include "sysprof-elf-symbolizer.h" +# include "sysprof-jitmap-symbolizer.h" # include "sysprof-kallsyms-symbolizer.h" # include "sysprof-mount.h" # include "sysprof-multi-symbolizer.h" diff --git a/src/libsysprof-analyze/sysprof-document-loader.c b/src/libsysprof-analyze/sysprof-document-loader.c index 44ba8669..b3876721 100644 --- a/src/libsysprof-analyze/sysprof-document-loader.c +++ b/src/libsysprof-analyze/sysprof-document-loader.c @@ -27,6 +27,7 @@ #include "sysprof-document-loader.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" @@ -150,6 +151,7 @@ set_default_symbolizer (SysprofDocumentLoader *self) sysprof_multi_symbolizer_take (multi, sysprof_bundled_symbolizer_new ()); 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 ()); self->symbolizer = SYSPROF_SYMBOLIZER (g_steal_pointer (&multi)); } diff --git a/src/libsysprof-analyze/sysprof-jitmap-symbolizer.c b/src/libsysprof-analyze/sysprof-jitmap-symbolizer.c new file mode 100644 index 00000000..5a31b6c7 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-jitmap-symbolizer.c @@ -0,0 +1,238 @@ +/* sysprof-jitmap-symbolizer.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-document-jitmap.h" +#include "sysprof-document-private.h" +#include "sysprof-jitmap-symbolizer.h" +#include "sysprof-symbol-private.h" +#include "sysprof-symbolizer-private.h" + +typedef struct _Jitmap +{ + SysprofAddress address; + GRefString *name; +} Jitmap; + +struct _SysprofJitmapSymbolizer +{ + SysprofSymbolizer parent_instance; + GArray *jitmaps; +}; + +struct _SysprofJitmapSymbolizerClass +{ + SysprofSymbolizerClass parent_class; +}; + +G_DEFINE_FINAL_TYPE (SysprofJitmapSymbolizer, sysprof_jitmap_symbolizer, SYSPROF_TYPE_SYMBOLIZER) + +typedef struct +{ + SysprofDocument *document; + GListModel *model; +} Prepare; + +static void +prepare_free (gpointer data) +{ + Prepare *prepare = data; + + g_clear_object (&prepare->model); + g_clear_object (&prepare->document); + g_free (prepare); +} + +static void +jitmap_clear (gpointer data) +{ + Jitmap *j = data; + g_clear_pointer (&j->name, g_ref_string_release); +} + +static int +sort_by_address (gconstpointer a, + gconstpointer b) +{ + const Jitmap *jitmap_a = a; + const Jitmap *jitmap_b = b; + + if (jitmap_a->address < jitmap_b->address) + return -1; + else if (jitmap_a->address > jitmap_b->address) + return 1; + else + return 0; +} + +static void +sysprof_jitmap_symbolizer_prepare_worker (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) +{ + SysprofJitmapSymbolizer *self = source_object; + Prepare *prepare = task_data; + guint n_jitmaps; + + g_assert (G_IS_TASK (task)); + g_assert (SYSPROF_IS_JITMAP_SYMBOLIZER (self)); + g_assert (prepare != NULL); + g_assert (SYSPROF_IS_DOCUMENT (prepare->document)); + g_assert (G_IS_LIST_MODEL (prepare->model)); + g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); + + n_jitmaps = g_list_model_get_n_items (prepare->model); + + for (guint i = 0; i < n_jitmaps; i++) + { + g_autoptr(SysprofDocumentJitmap) jitmap = g_list_model_get_item (prepare->model, i); + guint size = sysprof_document_jitmap_get_size (jitmap); + + for (guint j = 0; j < size; j++) + { + const char *name; + Jitmap map; + + if (!(name = sysprof_document_jitmap_get_mapping (jitmap, j, &map.address))) + continue; + + map.name = _sysprof_document_ref_string (prepare->document, name); + g_array_append_val (self->jitmaps, map); + } + } + + g_array_sort (self->jitmaps, sort_by_address); + + g_task_return_boolean (task, TRUE); +} + +static void +sysprof_jitmap_symbolizer_prepare_async (SysprofSymbolizer *symbolizer, + SysprofDocument *document, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + SysprofJitmapSymbolizer *self = (SysprofJitmapSymbolizer *)symbolizer; + g_autoptr(GTask) task = NULL; + Prepare *prepare; + + g_assert (SYSPROF_IS_JITMAP_SYMBOLIZER (self)); + g_assert (SYSPROF_IS_DOCUMENT (document)); + g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); + + prepare = g_new0 (Prepare, 1); + prepare->document = g_object_ref (document); + prepare->model = sysprof_document_list_jitmaps (document); + + task = g_task_new (self, cancellable, callback, user_data); + g_task_set_source_tag (task, sysprof_jitmap_symbolizer_prepare_async); + g_task_set_task_data (task, prepare, prepare_free); + g_task_run_in_thread (task, sysprof_jitmap_symbolizer_prepare_worker); +} + +static gboolean +sysprof_jitmap_symbolizer_prepare_finish (SysprofSymbolizer *symbolizer, + GAsyncResult *result, + GError **error) +{ + g_assert (SYSPROF_IS_JITMAP_SYMBOLIZER (symbolizer)); + g_assert (G_IS_TASK (result)); + g_assert (g_task_is_valid (result, symbolizer)); + + return g_task_propagate_boolean (G_TASK (result), error); +} + +static SysprofSymbol * +sysprof_jitmap_symbolizer_symbolize (SysprofSymbolizer *symbolizer, + SysprofStrings *strings, + const SysprofProcessInfo *process_info, + SysprofAddressContext context, + SysprofAddress address) +{ + SysprofJitmapSymbolizer *self = (SysprofJitmapSymbolizer *)symbolizer; + guint guess = (address & 0xFFFF) - 1; + + if (context != SYSPROF_ADDRESS_CONTEXT_NONE && + context != SYSPROF_ADDRESS_CONTEXT_USER) + return NULL; + + if ((address & 0xFFFFFFFF00000000) != 0xE000000000000000) + return NULL; + + /* Jitmap addresses generally start at 1 and work their way up + * monotonically (after masking off the high 0xE............... + * bits). So we can try for a fast index lookup to skip any sort + * of searching in the well behaved case. + */ + if G_LIKELY (guess < self->jitmaps->len) + { + const Jitmap *j = &g_array_index (self->jitmaps, Jitmap, guess); + + if G_LIKELY (j->address == address) + return _sysprof_symbol_new (g_ref_string_acquire (j->name), + NULL, + NULL, + j->address, + j->address + 1); + } + + /* TODO: Binary search for match */ + + return NULL; +} + +static void +sysprof_jitmap_symbolizer_finalize (GObject *object) +{ + SysprofJitmapSymbolizer *self = (SysprofJitmapSymbolizer *)object; + + g_clear_pointer (&self->jitmaps, g_array_unref); + + G_OBJECT_CLASS (sysprof_jitmap_symbolizer_parent_class)->finalize (object); +} + +static void +sysprof_jitmap_symbolizer_class_init (SysprofJitmapSymbolizerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + SysprofSymbolizerClass *symbolizer_class = SYSPROF_SYMBOLIZER_CLASS (klass); + + object_class->finalize = sysprof_jitmap_symbolizer_finalize; + + symbolizer_class->prepare_async = sysprof_jitmap_symbolizer_prepare_async; + symbolizer_class->prepare_finish = sysprof_jitmap_symbolizer_prepare_finish; + symbolizer_class->symbolize = sysprof_jitmap_symbolizer_symbolize; +} + +static void +sysprof_jitmap_symbolizer_init (SysprofJitmapSymbolizer *self) +{ + self->jitmaps = g_array_new (FALSE, FALSE, sizeof (Jitmap)); + g_array_set_clear_func (self->jitmaps, jitmap_clear); +} + +SysprofSymbolizer * +sysprof_jitmap_symbolizer_new (void) +{ + return g_object_new (SYSPROF_TYPE_JITMAP_SYMBOLIZER, NULL); +} diff --git a/src/libsysprof-analyze/sysprof-jitmap-symbolizer.h b/src/libsysprof-analyze/sysprof-jitmap-symbolizer.h new file mode 100644 index 00000000..5751eb46 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-jitmap-symbolizer.h @@ -0,0 +1,42 @@ +/* sysprof-jitmap-symbolizer.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-symbolizer.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_JITMAP_SYMBOLIZER (sysprof_jitmap_symbolizer_get_type()) +#define SYSPROF_IS_JITMAP_SYMBOLIZER(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_JITMAP_SYMBOLIZER) +#define SYSPROF_JITMAP_SYMBOLIZER(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_JITMAP_SYMBOLIZER, SysprofJitmapSymbolizer) +#define SYSPROF_JITMAP_SYMBOLIZER_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_JITMAP_SYMBOLIZER, SysprofJitmapSymbolizerClass) + +typedef struct _SysprofJitmapSymbolizer SysprofJitmapSymbolizer; +typedef struct _SysprofJitmapSymbolizerClass SysprofJitmapSymbolizerClass; + +SYSPROF_AVAILABLE_IN_ALL +GType sysprof_jitmap_symbolizer_get_type (void) G_GNUC_CONST; +SYSPROF_AVAILABLE_IN_ALL +SysprofSymbolizer *sysprof_jitmap_symbolizer_new (void); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofJitmapSymbolizer, g_object_unref) + +G_END_DECLS diff --git a/src/libsysprof-analyze/tests/test-symbolize.c b/src/libsysprof-analyze/tests/test-symbolize.c index c3850088..da98674e 100644 --- a/src/libsysprof-analyze/tests/test-symbolize.c +++ b/src/libsysprof-analyze/tests/test-symbolize.c @@ -132,8 +132,9 @@ main (int argc, { g_autoptr(SysprofMultiSymbolizer) multi = sysprof_multi_symbolizer_new (); - sysprof_multi_symbolizer_take (multi, sysprof_elf_symbolizer_new ()); 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 ()); sysprof_document_loader_set_symbolizer (loader, SYSPROF_SYMBOLIZER (multi)); } From fb80527d225e52581218f8e8b6cd5905bcd4e9bd Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 22 May 2023 13:30:04 -0700 Subject: [PATCH 0193/1030] libsysprof-analyze: bsearch jitmap for matching address --- .../sysprof-jitmap-symbolizer.c | 36 ++++++++++++------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-jitmap-symbolizer.c b/src/libsysprof-analyze/sysprof-jitmap-symbolizer.c index 5a31b6c7..3e08d870 100644 --- a/src/libsysprof-analyze/sysprof-jitmap-symbolizer.c +++ b/src/libsysprof-analyze/sysprof-jitmap-symbolizer.c @@ -20,6 +20,8 @@ #include "config.h" +#include + #include "sysprof-document-jitmap.h" #include "sysprof-document-private.h" #include "sysprof-jitmap-symbolizer.h" @@ -69,8 +71,8 @@ jitmap_clear (gpointer data) } static int -sort_by_address (gconstpointer a, - gconstpointer b) +compare_by_address (gconstpointer a, + gconstpointer b) { const Jitmap *jitmap_a = a; const Jitmap *jitmap_b = b; @@ -120,7 +122,7 @@ sysprof_jitmap_symbolizer_prepare_worker (GTask *task, } } - g_array_sort (self->jitmaps, sort_by_address); + g_array_sort (self->jitmaps, compare_by_address); g_task_return_boolean (task, TRUE); } @@ -170,7 +172,9 @@ sysprof_jitmap_symbolizer_symbolize (SysprofSymbolizer *symbolizer, SysprofAddress address) { SysprofJitmapSymbolizer *self = (SysprofJitmapSymbolizer *)symbolizer; + const Jitmap key = { address, NULL }; guint guess = (address & 0xFFFF) - 1; + const Jitmap *match; if (context != SYSPROF_ADDRESS_CONTEXT_NONE && context != SYSPROF_ADDRESS_CONTEXT_USER) @@ -186,19 +190,27 @@ sysprof_jitmap_symbolizer_symbolize (SysprofSymbolizer *symbolizer, */ if G_LIKELY (guess < self->jitmaps->len) { - const Jitmap *j = &g_array_index (self->jitmaps, Jitmap, guess); + match = &g_array_index (self->jitmaps, Jitmap, guess); - if G_LIKELY (j->address == address) - return _sysprof_symbol_new (g_ref_string_acquire (j->name), - NULL, - NULL, - j->address, - j->address + 1); + if G_LIKELY (match->address == address) + goto create_symbol; } - /* TODO: Binary search for match */ + match = bsearch (&key, + self->jitmaps->data, + self->jitmaps->len, + sizeof (Jitmap), + compare_by_address); - return NULL; + if (match == NULL) + return NULL; + +create_symbol: + return _sysprof_symbol_new (g_ref_string_acquire (match->name), + NULL, + NULL, + match->address, + match->address + 1); } static void From 57e7377c217fb23b6d1c67a984753b0d5c994308 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 22 May 2023 13:47:32 -0700 Subject: [PATCH 0194/1030] libsysprof-analyze: start indexing counter defines And add a SysprofDocumentCounter that will represent each counter. Still to do is implementing the ctrdef type and a way to transform those into the individual counters. --- src/libsysprof-analyze/meson.build | 2 + src/libsysprof-analyze/sysprof-analyze.h | 1 + .../sysprof-document-counter-private.h | 32 ++++ .../sysprof-document-counter.c | 153 ++++++++++++++++++ .../sysprof-document-counter.h | 43 +++++ src/libsysprof-analyze/sysprof-document.c | 27 ++++ 6 files changed, 258 insertions(+) create mode 100644 src/libsysprof-analyze/sysprof-document-counter-private.h create mode 100644 src/libsysprof-analyze/sysprof-document-counter.c create mode 100644 src/libsysprof-analyze/sysprof-document-counter.h diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build index c586f2bd..469104c9 100644 --- a/src/libsysprof-analyze/meson.build +++ b/src/libsysprof-analyze/meson.build @@ -2,6 +2,7 @@ libsysprof_analyze_public_sources = [ 'sysprof-bundled-symbolizer.c', 'sysprof-document.c', 'sysprof-document-allocation.c', + 'sysprof-document-counter.c', 'sysprof-document-exit.c', 'sysprof-document-file.c', 'sysprof-document-file-chunk.c', @@ -48,6 +49,7 @@ libsysprof_analyze_public_headers = [ 'sysprof-bundled-symbolizer.h', 'sysprof-document.h', 'sysprof-document-allocation.h', + 'sysprof-document-counter.h', 'sysprof-document-exit.h', 'sysprof-document-file.h', 'sysprof-document-file-chunk.h', diff --git a/src/libsysprof-analyze/sysprof-analyze.h b/src/libsysprof-analyze/sysprof-analyze.h index f2bf627b..2ac5ae6d 100644 --- a/src/libsysprof-analyze/sysprof-analyze.h +++ b/src/libsysprof-analyze/sysprof-analyze.h @@ -28,6 +28,7 @@ G_BEGIN_DECLS # include "sysprof-bundled-symbolizer.h" # include "sysprof-document.h" # include "sysprof-document-allocation.h" +# include "sysprof-document-counter.h" # include "sysprof-document-exit.h" # include "sysprof-document-file.h" # include "sysprof-document-file-chunk.h" diff --git a/src/libsysprof-analyze/sysprof-document-counter-private.h b/src/libsysprof-analyze/sysprof-document-counter-private.h new file mode 100644 index 00000000..ff3d27b5 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-document-counter-private.h @@ -0,0 +1,32 @@ +/* sysprof-document-counter-private.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-document-counter.h" + +G_BEGIN_DECLS + +SysprofDocumentCounter *_sysprof_document_counter_new (guint id, + GRefString *category, + GRefString *name, + GRefString *description); + +G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-document-counter.c b/src/libsysprof-analyze/sysprof-document-counter.c new file mode 100644 index 00000000..070775d6 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-document-counter.c @@ -0,0 +1,153 @@ +/* sysprof-document-counter.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-document-counter.h" + +struct _SysprofDocumentCounter +{ + GObject parent_instance; + GRefString *category; + GRefString *description; + GRefString *name; + guint id; +}; + +enum { + PROP_0, + PROP_CATEGORY, + PROP_DESCRIPTION, + PROP_ID, + PROP_NAME, + N_PROPS +}; + +G_DEFINE_FINAL_TYPE (SysprofDocumentCounter, sysprof_document_counter, G_TYPE_OBJECT) + +static GParamSpec *properties [N_PROPS]; + +static void +sysprof_document_counter_finalize (GObject *object) +{ + SysprofDocumentCounter *self = (SysprofDocumentCounter *)object; + + g_clear_pointer (&self->category, g_ref_string_release); + g_clear_pointer (&self->description, g_ref_string_release); + g_clear_pointer (&self->name, g_ref_string_release); + + G_OBJECT_CLASS (sysprof_document_counter_parent_class)->finalize (object); +} + +static void +sysprof_document_counter_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofDocumentCounter *self = SYSPROF_DOCUMENT_COUNTER (object); + + switch (prop_id) + { + case PROP_CATEGORY: + g_value_set_string (value, sysprof_document_counter_get_category (self)); + break; + + case PROP_DESCRIPTION: + g_value_set_string (value, sysprof_document_counter_get_description (self)); + break; + + case PROP_NAME: + g_value_set_string (value, sysprof_document_counter_get_name (self)); + break; + + case PROP_ID: + g_value_set_uint (value, sysprof_document_counter_get_id (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_document_counter_class_init (SysprofDocumentCounterClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = sysprof_document_counter_finalize; + object_class->get_property = sysprof_document_counter_get_property; + + g_object_class_install_properties (object_class, N_PROPS, properties); +} + +static void +sysprof_document_counter_init (SysprofDocumentCounter *self) +{ +} + +SysprofDocumentCounter * +_sysprof_document_counter_new (guint id, + GRefString *category, + GRefString *name, + GRefString *description) +{ + SysprofDocumentCounter *self; + + self = g_object_new (SYSPROF_TYPE_DOCUMENT_COUNTER, NULL); + self->id = id; + self->category = category; + self->name = name; + self->description = description; + + return self; +} + +const char * +sysprof_document_counter_get_category (SysprofDocumentCounter *self) +{ + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_COUNTER (self), NULL); + + return self->category; +} + +const char * +sysprof_document_counter_get_description (SysprofDocumentCounter *self) +{ + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_COUNTER (self), NULL); + + return self->description; +} + +const char * +sysprof_document_counter_get_name (SysprofDocumentCounter *self) +{ + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_COUNTER (self), NULL); + + return self->name; +} + +guint +sysprof_document_counter_get_id (SysprofDocumentCounter *self) +{ + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_COUNTER (self), 0); + + return self->id; +} diff --git a/src/libsysprof-analyze/sysprof-document-counter.h b/src/libsysprof-analyze/sysprof-document-counter.h new file mode 100644 index 00000000..0c669443 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-document-counter.h @@ -0,0 +1,43 @@ +/* sysprof-document-counter.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +#include + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_DOCUMENT_COUNTER (sysprof_document_counter_get_type()) + +SYSPROF_AVAILABLE_IN_ALL +G_DECLARE_FINAL_TYPE (SysprofDocumentCounter, sysprof_document_counter, SYSPROF, DOCUMENT_COUNTER, GObject) + +SYSPROF_AVAILABLE_IN_ALL +const char *sysprof_document_counter_get_category (SysprofDocumentCounter *self); +SYSPROF_AVAILABLE_IN_ALL +const char *sysprof_document_counter_get_description (SysprofDocumentCounter *self); +SYSPROF_AVAILABLE_IN_ALL +const char *sysprof_document_counter_get_name (SysprofDocumentCounter *self); +SYSPROF_AVAILABLE_IN_ALL +guint sysprof_document_counter_get_id (SysprofDocumentCounter *self); + +G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index 98656267..29ed7859 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -27,6 +27,7 @@ #include "sysprof-document-private.h" #include "sysprof-document-bitset-index-private.h" +#include "sysprof-document-counter-private.h" #include "sysprof-document-file-chunk.h" #include "sysprof-document-file-private.h" #include "sysprof-document-frame-private.h" @@ -60,6 +61,7 @@ struct _SysprofDocument GtkBitset *overlays; GtkBitset *pids; GtkBitset *jitmaps; + GtkBitset *ctrdefs; GHashTable *files_first_position; GHashTable *pid_to_process_info; @@ -198,6 +200,7 @@ sysprof_document_finalize (GObject *object) g_clear_pointer (&self->mapped_file, g_mapped_file_unref); g_clear_pointer (&self->frames, g_array_unref); + g_clear_pointer (&self->ctrdefs, gtk_bitset_unref); g_clear_pointer (&self->file_chunks, gtk_bitset_unref); g_clear_pointer (&self->jitmaps, gtk_bitset_unref); g_clear_pointer (&self->mmaps, gtk_bitset_unref); @@ -228,6 +231,7 @@ sysprof_document_init (SysprofDocument *self) self->frames = g_array_new (FALSE, FALSE, sizeof (SysprofDocumentFramePointer)); + self->ctrdefs = gtk_bitset_new_empty (); self->file_chunks = gtk_bitset_new_empty (); self->jitmaps = gtk_bitset_new_empty (); self->mmaps = gtk_bitset_new_empty (); @@ -424,6 +428,24 @@ sysprof_document_load_overlays (SysprofDocument *self) } } +static void +sysprof_document_load_counters (SysprofDocument *self) +{ + GtkBitsetIter iter; + guint i; + + g_assert (SYSPROF_IS_DOCUMENT (self)); + + if (gtk_bitset_iter_init_first (&iter, self->ctrdefs, &i)) + { + do + { + + } + while (gtk_bitset_iter_next (&iter, &i)); + } +} + static void sysprof_document_load_worker (GTask *task, gpointer source_object, @@ -506,6 +528,10 @@ sysprof_document_load_worker (GTask *task, gtk_bitset_add (self->file_chunks, self->frames->len); break; + case SYSPROF_CAPTURE_FRAME_CTRDEF: + gtk_bitset_add (self->ctrdefs, self->frames->len); + break; + case SYSPROF_CAPTURE_FRAME_JITMAP: gtk_bitset_add (self->jitmaps, self->frames->len); break; @@ -542,6 +568,7 @@ sysprof_document_load_worker (GTask *task, sysprof_document_load_mountinfos (self); sysprof_document_load_memory_maps (self); sysprof_document_load_overlays (self); + sysprof_document_load_counters (self); g_task_return_pointer (task, g_steal_pointer (&self), g_object_unref); } From 752393d449406371f7e2b736aa2e7cc369042b7b Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 22 May 2023 13:47:37 -0700 Subject: [PATCH 0195/1030] libsysprof-analyze: style cleanup --- src/libsysprof-analyze/sysprof-document-process.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-document-process.c b/src/libsysprof-analyze/sysprof-document-process.c index 3c7d814d..bb5feb04 100644 --- a/src/libsysprof-analyze/sysprof-document-process.c +++ b/src/libsysprof-analyze/sysprof-document-process.c @@ -57,9 +57,9 @@ sysprof_document_process_finalize (GObject *object) static void sysprof_document_process_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) + guint prop_id, + GValue *value, + GParamSpec *pspec) { SysprofDocumentProcess *self = SYSPROF_DOCUMENT_PROCESS (object); From 29cfad06ec52fbbbdd85a2db8368d3891fe5bec0 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 22 May 2023 14:12:53 -0700 Subject: [PATCH 0196/1030] libsysprof-analyze: add counter define document frames --- src/libsysprof-analyze/meson.build | 2 + src/libsysprof-analyze/sysprof-analyze.h | 1 + .../sysprof-document-ctrdef.c | 105 ++++++++++++++++++ .../sysprof-document-ctrdef.h | 53 +++++++++ .../sysprof-document-frame.c | 5 + .../tests/test-capture-model.c | 19 ++++ 6 files changed, 185 insertions(+) create mode 100644 src/libsysprof-analyze/sysprof-document-ctrdef.c create mode 100644 src/libsysprof-analyze/sysprof-document-ctrdef.h diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build index 469104c9..972a65a0 100644 --- a/src/libsysprof-analyze/meson.build +++ b/src/libsysprof-analyze/meson.build @@ -3,6 +3,7 @@ libsysprof_analyze_public_sources = [ 'sysprof-document.c', 'sysprof-document-allocation.c', 'sysprof-document-counter.c', + 'sysprof-document-ctrdef.c', 'sysprof-document-exit.c', 'sysprof-document-file.c', 'sysprof-document-file-chunk.c', @@ -50,6 +51,7 @@ libsysprof_analyze_public_headers = [ 'sysprof-document.h', 'sysprof-document-allocation.h', 'sysprof-document-counter.h', + 'sysprof-document-ctrdef.h', 'sysprof-document-exit.h', 'sysprof-document-file.h', 'sysprof-document-file-chunk.h', diff --git a/src/libsysprof-analyze/sysprof-analyze.h b/src/libsysprof-analyze/sysprof-analyze.h index 2ac5ae6d..5488e86d 100644 --- a/src/libsysprof-analyze/sysprof-analyze.h +++ b/src/libsysprof-analyze/sysprof-analyze.h @@ -29,6 +29,7 @@ G_BEGIN_DECLS # include "sysprof-document.h" # include "sysprof-document-allocation.h" # include "sysprof-document-counter.h" +# include "sysprof-document-ctrdef.h" # include "sysprof-document-exit.h" # include "sysprof-document-file.h" # include "sysprof-document-file-chunk.h" diff --git a/src/libsysprof-analyze/sysprof-document-ctrdef.c b/src/libsysprof-analyze/sysprof-document-ctrdef.c new file mode 100644 index 00000000..a6bba826 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-document-ctrdef.c @@ -0,0 +1,105 @@ +/* sysprof-document-ctrdef.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-document-frame-private.h" +#include "sysprof-document-ctrdef.h" + +struct _SysprofDocumentCtrdef +{ + SysprofDocumentFrame parent_instance; +}; + +struct _SysprofDocumentCtrdefClass +{ + SysprofDocumentFrameClass parent_class; +}; + +G_DEFINE_FINAL_TYPE (SysprofDocumentCtrdef, sysprof_document_ctrdef, SYSPROF_TYPE_DOCUMENT_FRAME) + +static void +sysprof_document_ctrdef_class_init (SysprofDocumentCtrdefClass *klass) +{ +} + +static void +sysprof_document_ctrdef_init (SysprofDocumentCtrdef *self) +{ +} + +guint +sysprof_document_ctrdef_get_n_counters (SysprofDocumentCtrdef *self) +{ + const SysprofCaptureCounterDefine *ctrdef; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_CTRDEF (self), 0); + + ctrdef = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureCounterDefine); + + return SYSPROF_DOCUMENT_FRAME_UINT16 (self, ctrdef->n_counters); +} + +/** + * sysprof_document_ctrdef_get_counter: + * @self: a #SysprofDocumentCtrdef + * @nth: a value between 0 and sysprof_document_ctrdef_get_n_counters() + * @id: (out): a location for the id of the counter + * @type: (out): a location for either %SYSPROF_CAPTURE_COUNTER_INT64 + * or %SYSPROF_CAPTURE_COUNTER_DOUBLE + * @category: (out): a location for the category + * @name: (out): a location for the name + * @description: (out): a location for the description + * + * Gets information about a counter defined in @self. + */ +void +sysprof_document_ctrdef_get_counter (SysprofDocumentCtrdef *self, + guint nth, + guint *id, + guint *type, + const char **category, + const char **name, + const char **description) +{ + const SysprofCaptureCounterDefine *ctrdef; + const SysprofCaptureCounter *ctr; + + g_return_if_fail (SYSPROF_IS_DOCUMENT_CTRDEF (self)); + g_return_if_fail (nth < sysprof_document_ctrdef_get_n_counters (self)); + + ctrdef = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureCounterDefine); + ctr = &ctrdef->counters[nth]; + + if (id != NULL) + *id = SYSPROF_DOCUMENT_FRAME_UINT32 (self, ctr->id); + + if (type != NULL) + *type = ctr->type; + + if (category != NULL) + *category = SYSPROF_DOCUMENT_FRAME_CSTRING (self, ctr->category); + + if (name != NULL) + *name = SYSPROF_DOCUMENT_FRAME_CSTRING (self, ctr->name); + + if (description != NULL) + *description = SYSPROF_DOCUMENT_FRAME_CSTRING (self, ctr->description); +} diff --git a/src/libsysprof-analyze/sysprof-document-ctrdef.h b/src/libsysprof-analyze/sysprof-document-ctrdef.h new file mode 100644 index 00000000..c8e1a652 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-document-ctrdef.h @@ -0,0 +1,53 @@ +/* sysprof-document-ctrdef.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +#include "sysprof-document-frame.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_DOCUMENT_CTRDEF (sysprof_document_ctrdef_get_type()) +#define SYSPROF_IS_DOCUMENT_CTRDEF(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_DOCUMENT_CTRDEF) +#define SYSPROF_DOCUMENT_CTRDEF(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_DOCUMENT_CTRDEF, SysprofDocumentCtrdef) +#define SYSPROF_DOCUMENT_CTRDEF_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_DOCUMENT_CTRDEF, SysprofDocumentCtrdefClass) + +typedef struct _SysprofDocumentCtrdef SysprofDocumentCtrdef; +typedef struct _SysprofDocumentCtrdefClass SysprofDocumentCtrdefClass; + +SYSPROF_AVAILABLE_IN_ALL +GType sysprof_document_ctrdef_get_type (void) G_GNUC_CONST; +SYSPROF_AVAILABLE_IN_ALL +guint sysprof_document_ctrdef_get_n_counters (SysprofDocumentCtrdef *self); +SYSPROF_AVAILABLE_IN_ALL +void sysprof_document_ctrdef_get_counter (SysprofDocumentCtrdef *self, + guint nth, + guint *id, + guint *type, + const char **category, + const char **name, + const char **description); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofDocumentCtrdef, g_object_unref) + +G_END_DECLS + diff --git a/src/libsysprof-analyze/sysprof-document-frame.c b/src/libsysprof-analyze/sysprof-document-frame.c index 09664eab..ee44b9b4 100644 --- a/src/libsysprof-analyze/sysprof-document-frame.c +++ b/src/libsysprof-analyze/sysprof-document-frame.c @@ -23,6 +23,7 @@ #include "sysprof-document-frame-private.h" #include "sysprof-document-allocation.h" +#include "sysprof-document-ctrdef.h" #include "sysprof-document-exit.h" #include "sysprof-document-file-chunk.h" #include "sysprof-document-fork.h" @@ -178,6 +179,10 @@ _sysprof_document_frame_new (GMappedFile *mapped_file, gtype = SYSPROF_TYPE_DOCUMENT_JITMAP; break; + case SYSPROF_CAPTURE_FRAME_CTRDEF: + gtype = SYSPROF_TYPE_DOCUMENT_CTRDEF; + break; + default: gtype = SYSPROF_TYPE_DOCUMENT_FRAME; break; diff --git a/src/libsysprof-analyze/tests/test-capture-model.c b/src/libsysprof-analyze/tests/test-capture-model.c index aa5dd0f9..ac94872f 100644 --- a/src/libsysprof-analyze/tests/test-capture-model.c +++ b/src/libsysprof-analyze/tests/test-capture-model.c @@ -84,6 +84,25 @@ main (int argc, else if (SYSPROF_IS_DOCUMENT_JITMAP (frame)) g_print (" n_jitmaps=%u", sysprof_document_jitmap_get_size (SYSPROF_DOCUMENT_JITMAP (frame))); + else if (SYSPROF_IS_DOCUMENT_CTRDEF (frame)) + { + guint n_counters = sysprof_document_ctrdef_get_n_counters (SYSPROF_DOCUMENT_CTRDEF (frame)); + + g_print (" n_counters=%u", n_counters); + + for (guint j = 0; j < n_counters; j++) + { + const char *category, *name; + guint id, type; + + sysprof_document_ctrdef_get_counter (SYSPROF_DOCUMENT_CTRDEF (frame), + j, &id, &type, &category, &name, NULL); + g_print (" [%u<%s>:%s.%s]", + id, + type == SYSPROF_CAPTURE_COUNTER_INT64 ? "i64" : "f64", + category, name); + } + } else if (SYSPROF_IS_DOCUMENT_ALLOCATION (frame)) { if (sysprof_document_allocation_is_free (SYSPROF_DOCUMENT_ALLOCATION (frame))) From aaf753096177cf93236d26b9f59b146a2ac0471b Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 22 May 2023 15:21:42 -0700 Subject: [PATCH 0197/1030] libsysprof-analyze: add value-type for counter --- .../sysprof-document-counter-private.h | 1 + .../sysprof-document-counter.c | 37 +++++++++++++++++++ .../sysprof-document-counter.h | 2 + 3 files changed, 40 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-document-counter-private.h b/src/libsysprof-analyze/sysprof-document-counter-private.h index ff3d27b5..ac925d37 100644 --- a/src/libsysprof-analyze/sysprof-document-counter-private.h +++ b/src/libsysprof-analyze/sysprof-document-counter-private.h @@ -25,6 +25,7 @@ G_BEGIN_DECLS SysprofDocumentCounter *_sysprof_document_counter_new (guint id, + guint type, GRefString *category, GRefString *name, GRefString *description); diff --git a/src/libsysprof-analyze/sysprof-document-counter.c b/src/libsysprof-analyze/sysprof-document-counter.c index 070775d6..aeea429b 100644 --- a/src/libsysprof-analyze/sysprof-document-counter.c +++ b/src/libsysprof-analyze/sysprof-document-counter.c @@ -29,6 +29,7 @@ struct _SysprofDocumentCounter GRefString *description; GRefString *name; guint id; + guint type; }; enum { @@ -95,6 +96,26 @@ sysprof_document_counter_class_init (SysprofDocumentCounterClass *klass) object_class->finalize = sysprof_document_counter_finalize; object_class->get_property = sysprof_document_counter_get_property; + properties [PROP_ID] = + g_param_spec_uint ("id", NULL, NULL, + 0, G_MAXUINT, 0, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + properties [PROP_CATEGORY] = + g_param_spec_string ("category", NULL, NULL, + NULL, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + properties [PROP_DESCRIPTION] = + g_param_spec_string ("description", NULL, NULL, + NULL, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + properties [PROP_NAME] = + g_param_spec_string ("name", NULL, NULL, + NULL, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_properties (object_class, N_PROPS, properties); } @@ -105,6 +126,7 @@ sysprof_document_counter_init (SysprofDocumentCounter *self) SysprofDocumentCounter * _sysprof_document_counter_new (guint id, + guint type, GRefString *category, GRefString *name, GRefString *description) @@ -113,6 +135,7 @@ _sysprof_document_counter_new (guint id, self = g_object_new (SYSPROF_TYPE_DOCUMENT_COUNTER, NULL); self->id = id; + self->type = type; self->category = category; self->name = name; self->description = description; @@ -151,3 +174,17 @@ sysprof_document_counter_get_id (SysprofDocumentCounter *self) return self->id; } + +GType +sysprof_document_counter_get_value_type (SysprofDocumentCounter *self) +{ + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_COUNTER (self), G_TYPE_INVALID); + + if (self->type == SYSPROF_CAPTURE_COUNTER_INT64) + return G_TYPE_INT64; + + if (self->type == SYSPROF_CAPTURE_COUNTER_DOUBLE) + return G_TYPE_DOUBLE; + + return G_TYPE_INVALID; +} diff --git a/src/libsysprof-analyze/sysprof-document-counter.h b/src/libsysprof-analyze/sysprof-document-counter.h index 0c669443..af3e1f7e 100644 --- a/src/libsysprof-analyze/sysprof-document-counter.h +++ b/src/libsysprof-analyze/sysprof-document-counter.h @@ -39,5 +39,7 @@ SYSPROF_AVAILABLE_IN_ALL const char *sysprof_document_counter_get_name (SysprofDocumentCounter *self); SYSPROF_AVAILABLE_IN_ALL guint sysprof_document_counter_get_id (SysprofDocumentCounter *self); +SYSPROF_AVAILABLE_IN_ALL +GType sysprof_document_counter_get_value_type (SysprofDocumentCounter *self); G_END_DECLS From 8f98af9b12a6fb16dbbb0b7db674f4911d9dc259 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 22 May 2023 15:22:35 -0700 Subject: [PATCH 0198/1030] libsysprof-analyze: index and list counters from capture --- src/libsysprof-analyze/sysprof-document.c | 44 +++++++++++++ src/libsysprof-analyze/sysprof-document.h | 2 + src/libsysprof-analyze/tests/meson.build | 1 + .../tests/test-list-counters.c | 66 +++++++++++++++++++ 4 files changed, 113 insertions(+) create mode 100644 src/libsysprof-analyze/tests/test-list-counters.c diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index 29ed7859..6e2adc30 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -28,6 +28,7 @@ #include "sysprof-document-bitset-index-private.h" #include "sysprof-document-counter-private.h" +#include "sysprof-document-ctrdef.h" #include "sysprof-document-file-chunk.h" #include "sysprof-document-file-private.h" #include "sysprof-document-frame-private.h" @@ -51,6 +52,7 @@ struct _SysprofDocument GArray *frames; GMappedFile *mapped_file; const guint8 *base; + GListStore *counters; SysprofStrings *strings; @@ -209,6 +211,7 @@ sysprof_document_finalize (GObject *object) g_clear_pointer (&self->processes, gtk_bitset_unref); g_clear_pointer (&self->traceables, gtk_bitset_unref); + g_clear_object (&self->counters); g_clear_object (&self->mount_namespace); g_clear_object (&self->symbols); @@ -231,6 +234,8 @@ sysprof_document_init (SysprofDocument *self) self->frames = g_array_new (FALSE, FALSE, sizeof (SysprofDocumentFramePointer)); + self->counters = g_list_store_new (SYSPROF_TYPE_DOCUMENT_COUNTER); + self->ctrdefs = gtk_bitset_new_empty (); self->file_chunks = gtk_bitset_new_empty (); self->jitmaps = gtk_bitset_new_empty (); @@ -431,18 +436,43 @@ sysprof_document_load_overlays (SysprofDocument *self) static void sysprof_document_load_counters (SysprofDocument *self) { + g_autoptr(GPtrArray) counters = NULL; GtkBitsetIter iter; guint i; g_assert (SYSPROF_IS_DOCUMENT (self)); + counters = g_ptr_array_new_with_free_func (g_object_unref); + if (gtk_bitset_iter_init_first (&iter, self->ctrdefs, &i)) { do { + g_autoptr(SysprofDocumentCtrdef) ctrdef = g_list_model_get_item (G_LIST_MODEL (self), i); + guint n_counters = sysprof_document_ctrdef_get_n_counters (ctrdef); + for (guint j = 0; j < n_counters; j++) + { + const char *category; + const char *name; + const char *description; + guint id; + guint type; + + sysprof_document_ctrdef_get_counter (ctrdef, j, &id, &type, &category, &name, &description); + + g_ptr_array_add (counters, + _sysprof_document_counter_new (id, + type, + sysprof_strings_get (self->strings, category), + sysprof_strings_get (self->strings, name), + sysprof_strings_get (self->strings, description))); + } } while (gtk_bitset_iter_next (&iter, &i)); + + if (counters->len > 0) + g_list_store_splice (self->counters, 0, 0, counters->pdata, counters->len); } } @@ -927,3 +957,17 @@ sysprof_document_symbolize_traceable (SysprofDocument *self, return n_addresses; } + +/** + * sysprof_document_list_counters: + * @self: a #SysprofDocument + * + * Returns: (transfer full): a #GListModel of #SysprofDocumentCounter + */ +GListModel * +sysprof_document_list_counters (SysprofDocument *self) +{ + g_return_val_if_fail (SYSPROF_IS_DOCUMENT (self), NULL); + + return g_object_ref (G_LIST_MODEL (self->counters)); +} diff --git a/src/libsysprof-analyze/sysprof-document.h b/src/libsysprof-analyze/sysprof-document.h index 465a203f..df2371e7 100644 --- a/src/libsysprof-analyze/sysprof-document.h +++ b/src/libsysprof-analyze/sysprof-document.h @@ -47,6 +47,8 @@ GListModel *sysprof_document_list_processes (SysprofDocument SYSPROF_AVAILABLE_IN_ALL GListModel *sysprof_document_list_jitmaps (SysprofDocument *self); SYSPROF_AVAILABLE_IN_ALL +GListModel *sysprof_document_list_counters (SysprofDocument *self); +SYSPROF_AVAILABLE_IN_ALL guint sysprof_document_symbolize_traceable (SysprofDocument *self, SysprofDocumentTraceable *traceable, SysprofSymbol **symbols, diff --git a/src/libsysprof-analyze/tests/meson.build b/src/libsysprof-analyze/tests/meson.build index 5220419c..cb859117 100644 --- a/src/libsysprof-analyze/tests/meson.build +++ b/src/libsysprof-analyze/tests/meson.build @@ -14,6 +14,7 @@ libsysprof_analyze_testsuite_c_args = [ libsysprof_analyze_testsuite = { 'test-capture-model' : {'skip': true}, 'test-elf-loader' : {'skip': true}, + 'test-list-counters' : {'skip': true}, 'test-list-files' : {'skip': true}, 'test-list-jitmap' : {'skip': true}, 'test-print-file' : {'skip': true}, diff --git a/src/libsysprof-analyze/tests/test-list-counters.c b/src/libsysprof-analyze/tests/test-list-counters.c new file mode 100644 index 00000000..fa54b1ae --- /dev/null +++ b/src/libsysprof-analyze/tests/test-list-counters.c @@ -0,0 +1,66 @@ +/* test-list-counters.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include + +#include "sysprof-document-private.h" + +int +main (int argc, + char *argv[]) +{ + g_autoptr(SysprofDocumentLoader) loader = NULL; + g_autoptr(SysprofDocument) document = NULL; + g_autoptr(GListModel) model = NULL; + g_autoptr(GError) error = NULL; + guint n_items; + + if (argc < 2) + { + g_printerr ("usage: %s CAPTURE_FILE\n", argv[0]); + return 1; + } + + loader = sysprof_document_loader_new (argv[1]); + sysprof_document_loader_set_symbolizer (loader, sysprof_no_symbolizer_get ()); + + if (!(document = sysprof_document_loader_load (loader, NULL, &error))) + { + g_printerr ("Failed to open capture: %s\n", error->message); + return 1; + } + + model = sysprof_document_list_counters (document); + n_items = g_list_model_get_n_items (model); + + for (guint i = 0; i < n_items; i++) + { + g_autoptr(SysprofDocumentCounter) counter = g_list_model_get_item (model, i); + + g_print ("%d<%s> %s.%s: %s\n", + sysprof_document_counter_get_id (counter), + sysprof_document_counter_get_value_type (counter) == G_TYPE_INT64 ? "i64" : "f64", + sysprof_document_counter_get_category (counter), + sysprof_document_counter_get_name (counter), + sysprof_document_counter_get_description (counter)); + } + + return 0; +} From c3a5771da00357151e1382c78bbbee9d55935141 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 22 May 2023 15:32:31 -0700 Subject: [PATCH 0199/1030] libsysprof-analyze: create arrays for counter values And assign them to the SysprofDocumentCounter so that they can pick them up once we index/add them during counter loading of ctrset. --- .../sysprof-document-counter-private.h | 3 ++- src/libsysprof-analyze/sysprof-document-counter.c | 6 +++++- src/libsysprof-analyze/sysprof-document.c | 14 +++++++++++++- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-document-counter-private.h b/src/libsysprof-analyze/sysprof-document-counter-private.h index ac925d37..821d2bbc 100644 --- a/src/libsysprof-analyze/sysprof-document-counter-private.h +++ b/src/libsysprof-analyze/sysprof-document-counter-private.h @@ -28,6 +28,7 @@ SysprofDocumentCounter *_sysprof_document_counter_new (guint id, guint type, GRefString *category, GRefString *name, - GRefString *description); + GRefString *description, + GArray *values); G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-document-counter.c b/src/libsysprof-analyze/sysprof-document-counter.c index aeea429b..7cdb838b 100644 --- a/src/libsysprof-analyze/sysprof-document-counter.c +++ b/src/libsysprof-analyze/sysprof-document-counter.c @@ -28,6 +28,7 @@ struct _SysprofDocumentCounter GRefString *category; GRefString *description; GRefString *name; + GArray *values; guint id; guint type; }; @@ -53,6 +54,7 @@ sysprof_document_counter_finalize (GObject *object) g_clear_pointer (&self->category, g_ref_string_release); g_clear_pointer (&self->description, g_ref_string_release); g_clear_pointer (&self->name, g_ref_string_release); + g_clear_pointer (&self->values, g_array_unref); G_OBJECT_CLASS (sysprof_document_counter_parent_class)->finalize (object); } @@ -129,7 +131,8 @@ _sysprof_document_counter_new (guint id, guint type, GRefString *category, GRefString *name, - GRefString *description) + GRefString *description, + GArray *values) { SysprofDocumentCounter *self; @@ -139,6 +142,7 @@ _sysprof_document_counter_new (guint id, self->category = category; self->name = name; self->description = description; + self->values = values; return self; } diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index 6e2adc30..9679044d 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -52,7 +52,9 @@ struct _SysprofDocument GArray *frames; GMappedFile *mapped_file; const guint8 *base; + GListStore *counters; + GHashTable *counter_id_to_values; SysprofStrings *strings; @@ -212,6 +214,8 @@ sysprof_document_finalize (GObject *object) g_clear_pointer (&self->traceables, gtk_bitset_unref); g_clear_object (&self->counters); + g_clear_pointer (&self->counter_id_to_values, g_hash_table_unref); + g_clear_object (&self->mount_namespace); g_clear_object (&self->symbols); @@ -235,6 +239,8 @@ sysprof_document_init (SysprofDocument *self) self->frames = g_array_new (FALSE, FALSE, sizeof (SysprofDocumentFramePointer)); self->counters = g_list_store_new (SYSPROF_TYPE_DOCUMENT_COUNTER); + self->counter_id_to_values = g_hash_table_new_full (NULL, NULL, NULL, + (GDestroyNotify)g_array_unref); self->ctrdefs = gtk_bitset_new_empty (); self->file_chunks = gtk_bitset_new_empty (); @@ -453,6 +459,7 @@ sysprof_document_load_counters (SysprofDocument *self) for (guint j = 0; j < n_counters; j++) { + g_autoptr(GArray) values = g_array_new (FALSE, FALSE, 8); const char *category; const char *name; const char *description; @@ -461,12 +468,17 @@ sysprof_document_load_counters (SysprofDocument *self) sysprof_document_ctrdef_get_counter (ctrdef, j, &id, &type, &category, &name, &description); + g_hash_table_insert (self->counter_id_to_values, + GUINT_TO_POINTER (id), + g_array_ref (values)); + g_ptr_array_add (counters, _sysprof_document_counter_new (id, type, sysprof_strings_get (self->strings, category), sysprof_strings_get (self->strings, name), - sysprof_strings_get (self->strings, description))); + sysprof_strings_get (self->strings, description), + g_steal_pointer (&values))); } } while (gtk_bitset_iter_next (&iter, &i)); From 9d2f7d376770fe7751113149b0ed7c5dd3e81ce4 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 22 May 2023 16:03:46 -0700 Subject: [PATCH 0200/1030] libsysprof-analyze: add SysprofDocumentCtrset This represents the SysprofCaptureCtrset and allows fetching the raw values for a given counter. These are raw because they may not be endian swapped and that is the responsibility of the consumer. SysprofDocument will use these eventually to store the values for a given counter and the time of the value shift. --- src/libsysprof-analyze/meson.build | 2 + src/libsysprof-analyze/sysprof-analyze.h | 1 + .../sysprof-document-ctrset.c | 88 +++++++++++++++++++ .../sysprof-document-ctrset.h | 50 +++++++++++ .../sysprof-document-frame.c | 5 ++ src/libsysprof-analyze/sysprof-document.c | 8 ++ 6 files changed, 154 insertions(+) create mode 100644 src/libsysprof-analyze/sysprof-document-ctrset.c create mode 100644 src/libsysprof-analyze/sysprof-document-ctrset.h diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build index 972a65a0..82028017 100644 --- a/src/libsysprof-analyze/meson.build +++ b/src/libsysprof-analyze/meson.build @@ -4,6 +4,7 @@ libsysprof_analyze_public_sources = [ 'sysprof-document-allocation.c', 'sysprof-document-counter.c', 'sysprof-document-ctrdef.c', + 'sysprof-document-ctrset.c', 'sysprof-document-exit.c', 'sysprof-document-file.c', 'sysprof-document-file-chunk.c', @@ -52,6 +53,7 @@ libsysprof_analyze_public_headers = [ 'sysprof-document-allocation.h', 'sysprof-document-counter.h', 'sysprof-document-ctrdef.h', + 'sysprof-document-ctrset.h', 'sysprof-document-exit.h', 'sysprof-document-file.h', 'sysprof-document-file-chunk.h', diff --git a/src/libsysprof-analyze/sysprof-analyze.h b/src/libsysprof-analyze/sysprof-analyze.h index 5488e86d..80c88fa7 100644 --- a/src/libsysprof-analyze/sysprof-analyze.h +++ b/src/libsysprof-analyze/sysprof-analyze.h @@ -30,6 +30,7 @@ G_BEGIN_DECLS # include "sysprof-document-allocation.h" # include "sysprof-document-counter.h" # include "sysprof-document-ctrdef.h" +# include "sysprof-document-ctrset.h" # include "sysprof-document-exit.h" # include "sysprof-document-file.h" # include "sysprof-document-file-chunk.h" diff --git a/src/libsysprof-analyze/sysprof-document-ctrset.c b/src/libsysprof-analyze/sysprof-document-ctrset.c new file mode 100644 index 00000000..de083deb --- /dev/null +++ b/src/libsysprof-analyze/sysprof-document-ctrset.c @@ -0,0 +1,88 @@ +/* sysprof-document-ctrset.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-document-frame-private.h" +#include "sysprof-document-ctrset.h" + +struct _SysprofDocumentCtrset +{ + SysprofDocumentFrame parent_instance; +}; + +struct _SysprofDocumentCtrsetClass +{ + SysprofDocumentFrameClass parent_class; +}; + +G_DEFINE_FINAL_TYPE (SysprofDocumentCtrset, sysprof_document_ctrset, SYSPROF_TYPE_DOCUMENT_FRAME) + +static void +sysprof_document_ctrset_class_init (SysprofDocumentCtrsetClass *klass) +{ +} + +static void +sysprof_document_ctrset_init (SysprofDocumentCtrset *self) +{ +} + +guint +sysprof_document_ctrset_get_n_values (SysprofDocumentCtrset *self) +{ + const SysprofCaptureCounterSet *ctrset; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_CTRSET (self), 0); + + ctrset = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureCounterSet); + + return SYSPROF_DOCUMENT_FRAME_UINT16 (self, ctrset->n_values); +} + +/** + * sysprof_document_ctrset_get_raw_value: (skip) + * @self: a #SysprofDocumentCtrset + * @nth: the nth value to get + * @id: (out): a location for the counter id + * @value: a location to store the raw value + * + * The raw value is 8 bytes and may not be converted to local + * byte ordering. + * + * @nth must be less-than sysprof_document_ctrset_get_n_values(). + */ +void +sysprof_document_ctrset_get_raw_value (SysprofDocumentCtrset *self, + guint nth, + guint *id, + guint8 value[restrict 8]) +{ + const SysprofCaptureCounterSet *ctrset; + + g_return_if_fail (SYSPROF_IS_DOCUMENT_CTRSET (self)); + g_return_if_fail (nth < sysprof_document_ctrset_get_n_values (self)); + g_return_if_fail (value != NULL); + + ctrset = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureCounterSet); + + *id = ctrset->values[nth / 8].ids[nth % 8]; + memcpy (value, &ctrset->values[nth / 8].values[nth % 8], 8); +} diff --git a/src/libsysprof-analyze/sysprof-document-ctrset.h b/src/libsysprof-analyze/sysprof-document-ctrset.h new file mode 100644 index 00000000..3bc6cc32 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-document-ctrset.h @@ -0,0 +1,50 @@ +/* sysprof-document-ctrset.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +#include "sysprof-document-frame.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_DOCUMENT_CTRSET (sysprof_document_ctrset_get_type()) +#define SYSPROF_IS_DOCUMENT_CTRSET(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_DOCUMENT_CTRSET) +#define SYSPROF_DOCUMENT_CTRSET(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_DOCUMENT_CTRSET, SysprofDocumentCtrset) +#define SYSPROF_DOCUMENT_CTRSET_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_DOCUMENT_CTRSET, SysprofDocumentCtrsetClass) + +typedef struct _SysprofDocumentCtrset SysprofDocumentCtrset; +typedef struct _SysprofDocumentCtrsetClass SysprofDocumentCtrsetClass; + +SYSPROF_AVAILABLE_IN_ALL +GType sysprof_document_ctrset_get_type (void) G_GNUC_CONST; +SYSPROF_AVAILABLE_IN_ALL +guint sysprof_document_ctrset_get_n_values (SysprofDocumentCtrset *self); +SYSPROF_AVAILABLE_IN_ALL +void sysprof_document_ctrset_get_raw_value (SysprofDocumentCtrset *self, + guint nth, + guint *id, + guint8 value[restrict 8]); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofDocumentCtrset, g_object_unref) + +G_END_DECLS + diff --git a/src/libsysprof-analyze/sysprof-document-frame.c b/src/libsysprof-analyze/sysprof-document-frame.c index ee44b9b4..f52022d5 100644 --- a/src/libsysprof-analyze/sysprof-document-frame.c +++ b/src/libsysprof-analyze/sysprof-document-frame.c @@ -24,6 +24,7 @@ #include "sysprof-document-allocation.h" #include "sysprof-document-ctrdef.h" +#include "sysprof-document-ctrset.h" #include "sysprof-document-exit.h" #include "sysprof-document-file-chunk.h" #include "sysprof-document-fork.h" @@ -183,6 +184,10 @@ _sysprof_document_frame_new (GMappedFile *mapped_file, gtype = SYSPROF_TYPE_DOCUMENT_CTRDEF; break; + case SYSPROF_CAPTURE_FRAME_CTRSET: + gtype = SYSPROF_TYPE_DOCUMENT_CTRSET; + break; + default: gtype = SYSPROF_TYPE_DOCUMENT_FRAME; break; diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index 9679044d..70f405c8 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -29,6 +29,7 @@ #include "sysprof-document-bitset-index-private.h" #include "sysprof-document-counter-private.h" #include "sysprof-document-ctrdef.h" +#include "sysprof-document-ctrset.h" #include "sysprof-document-file-chunk.h" #include "sysprof-document-file-private.h" #include "sysprof-document-frame-private.h" @@ -66,6 +67,7 @@ struct _SysprofDocument GtkBitset *pids; GtkBitset *jitmaps; GtkBitset *ctrdefs; + GtkBitset *ctrsets; GHashTable *files_first_position; GHashTable *pid_to_process_info; @@ -205,6 +207,7 @@ sysprof_document_finalize (GObject *object) g_clear_pointer (&self->frames, g_array_unref); g_clear_pointer (&self->ctrdefs, gtk_bitset_unref); + g_clear_pointer (&self->ctrsets, gtk_bitset_unref); g_clear_pointer (&self->file_chunks, gtk_bitset_unref); g_clear_pointer (&self->jitmaps, gtk_bitset_unref); g_clear_pointer (&self->mmaps, gtk_bitset_unref); @@ -243,6 +246,7 @@ sysprof_document_init (SysprofDocument *self) (GDestroyNotify)g_array_unref); self->ctrdefs = gtk_bitset_new_empty (); + self->ctrsets = gtk_bitset_new_empty (); self->file_chunks = gtk_bitset_new_empty (); self->jitmaps = gtk_bitset_new_empty (); self->mmaps = gtk_bitset_new_empty (); @@ -574,6 +578,10 @@ sysprof_document_load_worker (GTask *task, gtk_bitset_add (self->ctrdefs, self->frames->len); break; + case SYSPROF_CAPTURE_FRAME_CTRSET: + gtk_bitset_add (self->ctrsets, self->frames->len); + break; + case SYSPROF_CAPTURE_FRAME_JITMAP: gtk_bitset_add (self->jitmaps, self->frames->len); break; From 227362ea09dff3bb3e81066e11c923df5f6c8049 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 22 May 2023 16:04:29 -0700 Subject: [PATCH 0201/1030] libsysprof-analyze: load counter values with document --- .../sysprof-document-private.h | 10 +++ src/libsysprof-analyze/sysprof-document.c | 62 ++++++++++++++++++- 2 files changed, 69 insertions(+), 3 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-document-private.h b/src/libsysprof-analyze/sysprof-document-private.h index 75f4a783..5dec04ef 100644 --- a/src/libsysprof-analyze/sysprof-document-private.h +++ b/src/libsysprof-analyze/sysprof-document-private.h @@ -27,6 +27,16 @@ G_BEGIN_DECLS +typedef struct _SysprofDocumentCounterValue +{ + gint64 time; + union { + gint64 v_int64; + double v_double; + guint8 v_raw[8]; + }; +} SysprofDocumentCounterValue; + void _sysprof_document_new_async (GMappedFile *mapped_file, GCancellable *cancellable, GAsyncReadyCallback callback, diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index 70f405c8..93bf6769 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -447,23 +447,39 @@ static void sysprof_document_load_counters (SysprofDocument *self) { g_autoptr(GPtrArray) counters = NULL; + g_autoptr(GtkBitset) swap_ids = NULL; + GListModel *model; GtkBitsetIter iter; guint i; g_assert (SYSPROF_IS_DOCUMENT (self)); - counters = g_ptr_array_new_with_free_func (g_object_unref); + /* Cast up front to avoid many type checks later */ + model = G_LIST_MODEL (self); + /* If we need to swap int64 (double does not need swapping), then keep + * a bitset of which counters need swapping. That way we have a fast + * lookup below we can use when reading in counter values. + */ + if (self->needs_swap) + swap_ids = gtk_bitset_new_empty (); + + /* First create our counter objects which we will use to hold values that + * were extracted from the capture file. We create the array first so that + * we can use g_list_store_splice() rather than have signal emission for + * each and every added counter. + */ + counters = g_ptr_array_new_with_free_func (g_object_unref); if (gtk_bitset_iter_init_first (&iter, self->ctrdefs, &i)) { do { - g_autoptr(SysprofDocumentCtrdef) ctrdef = g_list_model_get_item (G_LIST_MODEL (self), i); + g_autoptr(SysprofDocumentCtrdef) ctrdef = g_list_model_get_item (model, i); guint n_counters = sysprof_document_ctrdef_get_n_counters (ctrdef); for (guint j = 0; j < n_counters; j++) { - g_autoptr(GArray) values = g_array_new (FALSE, FALSE, 8); + g_autoptr(GArray) values = g_array_new (FALSE, FALSE, sizeof (SysprofDocumentCounterValue)); const char *category; const char *name; const char *description; @@ -472,6 +488,10 @@ sysprof_document_load_counters (SysprofDocument *self) sysprof_document_ctrdef_get_counter (ctrdef, j, &id, &type, &category, &name, &description); + /* Keep track if this counter will need int64 endian swaps */ + if (swap_ids != NULL && type == SYSPROF_CAPTURE_COUNTER_INT64) + gtk_bitset_add (swap_ids, id); + g_hash_table_insert (self->counter_id_to_values, GUINT_TO_POINTER (id), g_array_ref (values)); @@ -490,6 +510,42 @@ sysprof_document_load_counters (SysprofDocument *self) if (counters->len > 0) g_list_store_splice (self->counters, 0, 0, counters->pdata, counters->len); } + + /* Now find all the counter values and associate them with the counters + * that were previously defined. + */ + if (gtk_bitset_iter_init_first (&iter, self->ctrsets, &i)) + { + do + { + g_autoptr(SysprofDocumentCtrset) ctrset = g_list_model_get_item (model, i); + guint n_values = sysprof_document_ctrset_get_n_values (ctrset); + SysprofDocumentCounterValue ctrval; + + ctrval.time = sysprof_document_frame_get_time (SYSPROF_DOCUMENT_FRAME (ctrset)); + + for (guint j = 0; j < n_values; j++) + { + GArray *values; + guint id; + + sysprof_document_ctrset_get_raw_value (ctrset, j, &id, ctrval.v_raw); + + if (swap_ids != NULL && gtk_bitset_contains (swap_ids, id)) + { +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + ctrval.v_int64 = GINT64_FROM_BE (ctrval.v_int64); +#else + ctrval.v_int64 = GINT64_FROM_LE (ctrval.v_int64); +#endif + } + + if ((values = g_hash_table_lookup (self->counter_id_to_values, GUINT_TO_POINTER (id)))) + g_array_append_val (values, ctrval); + } + } + while (gtk_bitset_iter_next (&iter, &i)); + } } static void From d510dd50da3650281c204aa0cb2234f7aa815c13 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 22 May 2023 16:46:36 -0700 Subject: [PATCH 0202/1030] libsysprof-analyze: add support for getting counter values --- src/libsysprof-analyze/meson.build | 2 + src/libsysprof-analyze/sysprof-analyze.h | 1 + .../sysprof-document-counter-value-private.h | 30 +++++ .../sysprof-document-counter-value.c | 124 ++++++++++++++++++ .../sysprof-document-counter-value.h | 45 +++++++ .../sysprof-document-counter.c | 98 +++++++++++++- .../sysprof-document-counter.h | 25 +++- .../sysprof-document-private.h | 4 +- src/libsysprof-analyze/sysprof-document.c | 4 +- .../tests/test-list-counters.c | 41 ++++++ 10 files changed, 364 insertions(+), 10 deletions(-) create mode 100644 src/libsysprof-analyze/sysprof-document-counter-value-private.h create mode 100644 src/libsysprof-analyze/sysprof-document-counter-value.c create mode 100644 src/libsysprof-analyze/sysprof-document-counter-value.h diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build index 82028017..00e3f408 100644 --- a/src/libsysprof-analyze/meson.build +++ b/src/libsysprof-analyze/meson.build @@ -3,6 +3,7 @@ libsysprof_analyze_public_sources = [ 'sysprof-document.c', 'sysprof-document-allocation.c', 'sysprof-document-counter.c', + 'sysprof-document-counter-value.c', 'sysprof-document-ctrdef.c', 'sysprof-document-ctrset.c', 'sysprof-document-exit.c', @@ -52,6 +53,7 @@ libsysprof_analyze_public_headers = [ 'sysprof-document.h', 'sysprof-document-allocation.h', 'sysprof-document-counter.h', + 'sysprof-document-counter-value.h', 'sysprof-document-ctrdef.h', 'sysprof-document-ctrset.h', 'sysprof-document-exit.h', diff --git a/src/libsysprof-analyze/sysprof-analyze.h b/src/libsysprof-analyze/sysprof-analyze.h index 80c88fa7..2962fc67 100644 --- a/src/libsysprof-analyze/sysprof-analyze.h +++ b/src/libsysprof-analyze/sysprof-analyze.h @@ -29,6 +29,7 @@ G_BEGIN_DECLS # include "sysprof-document.h" # include "sysprof-document-allocation.h" # include "sysprof-document-counter.h" +# include "sysprof-document-counter-value.h" # include "sysprof-document-ctrdef.h" # include "sysprof-document-ctrset.h" # include "sysprof-document-exit.h" diff --git a/src/libsysprof-analyze/sysprof-document-counter-value-private.h b/src/libsysprof-analyze/sysprof-document-counter-value-private.h new file mode 100644 index 00000000..bc7319ae --- /dev/null +++ b/src/libsysprof-analyze/sysprof-document-counter-value-private.h @@ -0,0 +1,30 @@ +/* sysprof-document-counter-value-private.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-document-counter-value.h" +#include "sysprof-document-private.h" + +G_BEGIN_DECLS + +SysprofDocumentCounterValue *_sysprof_document_counter_value_new (const SysprofDocumentTimedValue *value); + +G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-document-counter-value.c b/src/libsysprof-analyze/sysprof-document-counter-value.c new file mode 100644 index 00000000..8be6e53c --- /dev/null +++ b/src/libsysprof-analyze/sysprof-document-counter-value.c @@ -0,0 +1,124 @@ +/* sysprof-document-counter-value.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-document-counter-value-private.h" + +struct _SysprofDocumentCounterValue +{ + GObject parent_instance; + SysprofDocumentTimedValue value; +}; + +enum { + PROP_0, + PROP_TIME, + N_PROPS +}; + +G_DEFINE_FINAL_TYPE (SysprofDocumentCounterValue, sysprof_document_counter_value, G_TYPE_OBJECT) + +static GParamSpec *properties [N_PROPS]; + +static void +sysprof_document_counter_value_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofDocumentCounterValue *self = SYSPROF_DOCUMENT_COUNTER_VALUE (object); + + switch (prop_id) + { + case PROP_TIME: + g_value_set_int64 (value, sysprof_document_counter_value_get_time (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_document_counter_value_class_init (SysprofDocumentCounterValueClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->get_property = sysprof_document_counter_value_get_property; + + properties [PROP_TIME] = + g_param_spec_int64 ("time", NULL, NULL, + G_MININT64, 0, G_MAXINT64, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); +} + +static void +sysprof_document_counter_value_init (SysprofDocumentCounterValue *self) +{ +} + +SysprofDocumentCounterValue * +_sysprof_document_counter_value_new (const SysprofDocumentTimedValue *value) +{ + SysprofDocumentCounterValue *self; + + self = g_object_new (SYSPROF_TYPE_DOCUMENT_COUNTER_VALUE, NULL); + self->value = *value; + + return self; +} + +gint64 +sysprof_document_counter_value_get_time (SysprofDocumentCounterValue *self) +{ + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_COUNTER_VALUE (self), 0); + + return self->value.time; +} + +void +sysprof_document_counter_value_get_value (SysprofDocumentCounterValue *self, + GValue *value) +{ + g_return_if_fail (SYSPROF_IS_DOCUMENT_COUNTER_VALUE (self)); + g_return_if_fail (G_IS_VALUE (value)); + + if (G_VALUE_HOLDS_INT64 (value)) + g_value_set_int64 (value, self->value.v_int64); + else if (G_VALUE_HOLDS_DOUBLE (value)) + g_value_set_double (value, self->value.v_double); + else + g_warning_once ("Unsupported value type %s", G_VALUE_TYPE_NAME (value)); +} + +gint64 +sysprof_document_counter_value_get_value_int64 (SysprofDocumentCounterValue *self) +{ + return self->value.v_int64; +} + +double +sysprof_document_counter_value_get_value_double (SysprofDocumentCounterValue *self) +{ + return self->value.v_double; +} diff --git a/src/libsysprof-analyze/sysprof-document-counter-value.h b/src/libsysprof-analyze/sysprof-document-counter-value.h new file mode 100644 index 00000000..d780baf8 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-document-counter-value.h @@ -0,0 +1,45 @@ +/* sysprof-document-counter-value.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +#include + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_DOCUMENT_COUNTER_VALUE (sysprof_document_counter_value_get_type()) + +SYSPROF_AVAILABLE_IN_ALL +G_DECLARE_FINAL_TYPE (SysprofDocumentCounterValue, sysprof_document_counter_value, SYSPROF, DOCUMENT_COUNTER_VALUE, GObject) + +SYSPROF_AVAILABLE_IN_ALL +gint64 sysprof_document_counter_value_get_time (SysprofDocumentCounterValue *self); +SYSPROF_AVAILABLE_IN_ALL +void sysprof_document_counter_value_get_value (SysprofDocumentCounterValue *self, + GValue *value); +SYSPROF_AVAILABLE_IN_ALL +gint64 sysprof_document_counter_value_get_value_int64 (SysprofDocumentCounterValue *self); +SYSPROF_AVAILABLE_IN_ALL +double sysprof_document_counter_value_get_value_double (SysprofDocumentCounterValue *self); + + +G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-document-counter.c b/src/libsysprof-analyze/sysprof-document-counter.c index 7cdb838b..e6b31a5a 100644 --- a/src/libsysprof-analyze/sysprof-document-counter.c +++ b/src/libsysprof-analyze/sysprof-document-counter.c @@ -21,6 +21,8 @@ #include "config.h" #include "sysprof-document-counter.h" +#include "sysprof-document-counter-value-private.h" +#include "sysprof-document-private.h" struct _SysprofDocumentCounter { @@ -42,7 +44,43 @@ enum { N_PROPS }; -G_DEFINE_FINAL_TYPE (SysprofDocumentCounter, sysprof_document_counter, G_TYPE_OBJECT) +static guint +sysprof_document_counter_get_n_items (GListModel *model) +{ + return sysprof_document_counter_get_n_values (SYSPROF_DOCUMENT_COUNTER (model)); +} + +static GType +sysprof_document_counter_get_item_type (GListModel *model) +{ + return SYSPROF_TYPE_DOCUMENT_COUNTER_VALUE; +} + +static gpointer +sysprof_document_counter_get_item (GListModel *model, + guint position) +{ + SysprofDocumentCounter *self = SYSPROF_DOCUMENT_COUNTER (model); + const SysprofDocumentTimedValue *value; + + if (position >= self->values->len) + return NULL; + + value = &g_array_index (self->values, SysprofDocumentTimedValue, position); + + return _sysprof_document_counter_value_new (value); +} + +static void +list_model_iface_init (GListModelInterface *iface) +{ + iface->get_n_items = sysprof_document_counter_get_n_items; + iface->get_item_type = sysprof_document_counter_get_item_type; + iface->get_item = sysprof_document_counter_get_item; +} + +G_DEFINE_FINAL_TYPE_WITH_CODE (SysprofDocumentCounter, sysprof_document_counter, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init)) static GParamSpec *properties [N_PROPS]; @@ -192,3 +230,61 @@ sysprof_document_counter_get_value_type (SysprofDocumentCounter *self) return G_TYPE_INVALID; } + +guint +sysprof_document_counter_get_n_values (SysprofDocumentCounter *self) +{ + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_COUNTER (self), 0); + + return self->values->len; +} + +void +sysprof_document_counter_get_value (SysprofDocumentCounter *self, + guint nth, + gint64 *time, + GValue *value) +{ + g_return_if_fail (SYSPROF_IS_DOCUMENT_COUNTER (self)); + g_return_if_fail (nth < self->values->len); + g_return_if_fail (value == NULL || G_IS_VALUE (value)); + + if (time != NULL) + *time = g_array_index (self->values, SysprofDocumentTimedValue, nth).time; + + if (value == NULL) + return; + + if (G_VALUE_HOLDS_INT64 (value)) + g_value_set_int64 (value, g_array_index (self->values, SysprofDocumentTimedValue, nth).v_int64); + else if (G_VALUE_HOLDS_DOUBLE (value)) + g_value_set_double (value, g_array_index (self->values, SysprofDocumentTimedValue, nth).v_double); +} + +gint64 +sysprof_document_counter_get_value_int64 (SysprofDocumentCounter *self, + guint nth, + gint64 *time) +{ + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_COUNTER (self), 0); + g_return_val_if_fail (nth < self->values->len, 0); + + if (time != NULL) + *time = g_array_index (self->values, SysprofDocumentTimedValue, nth).time; + + return g_array_index (self->values, SysprofDocumentTimedValue, nth).v_int64; +} + +double +sysprof_document_counter_get_value_double (SysprofDocumentCounter *self, + guint nth, + gint64 *time) +{ + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_COUNTER (self), 0); + g_return_val_if_fail (nth < self->values->len, 0); + + if (time != NULL) + *time = g_array_index (self->values, SysprofDocumentTimedValue, nth).time; + + return g_array_index (self->values, SysprofDocumentTimedValue, nth).v_double; +} diff --git a/src/libsysprof-analyze/sysprof-document-counter.h b/src/libsysprof-analyze/sysprof-document-counter.h index af3e1f7e..cf796e38 100644 --- a/src/libsysprof-analyze/sysprof-document-counter.h +++ b/src/libsysprof-analyze/sysprof-document-counter.h @@ -32,14 +32,29 @@ SYSPROF_AVAILABLE_IN_ALL G_DECLARE_FINAL_TYPE (SysprofDocumentCounter, sysprof_document_counter, SYSPROF, DOCUMENT_COUNTER, GObject) SYSPROF_AVAILABLE_IN_ALL -const char *sysprof_document_counter_get_category (SysprofDocumentCounter *self); +const char *sysprof_document_counter_get_category (SysprofDocumentCounter *self); SYSPROF_AVAILABLE_IN_ALL -const char *sysprof_document_counter_get_description (SysprofDocumentCounter *self); +const char *sysprof_document_counter_get_description (SysprofDocumentCounter *self); SYSPROF_AVAILABLE_IN_ALL -const char *sysprof_document_counter_get_name (SysprofDocumentCounter *self); +const char *sysprof_document_counter_get_name (SysprofDocumentCounter *self); SYSPROF_AVAILABLE_IN_ALL -guint sysprof_document_counter_get_id (SysprofDocumentCounter *self); +guint sysprof_document_counter_get_id (SysprofDocumentCounter *self); SYSPROF_AVAILABLE_IN_ALL -GType sysprof_document_counter_get_value_type (SysprofDocumentCounter *self); +GType sysprof_document_counter_get_value_type (SysprofDocumentCounter *self); +SYSPROF_AVAILABLE_IN_ALL +guint sysprof_document_counter_get_n_values (SysprofDocumentCounter *self); +SYSPROF_AVAILABLE_IN_ALL +void sysprof_document_counter_get_value (SysprofDocumentCounter *self, + guint nth, + gint64 *time, + GValue *value); +SYSPROF_AVAILABLE_IN_ALL +gint64 sysprof_document_counter_get_value_int64 (SysprofDocumentCounter *self, + guint nth, + gint64 *time); +SYSPROF_AVAILABLE_IN_ALL +double sysprof_document_counter_get_value_double (SysprofDocumentCounter *self, + guint nth, + gint64 *time); G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-document-private.h b/src/libsysprof-analyze/sysprof-document-private.h index 5dec04ef..5f93ba07 100644 --- a/src/libsysprof-analyze/sysprof-document-private.h +++ b/src/libsysprof-analyze/sysprof-document-private.h @@ -27,7 +27,7 @@ G_BEGIN_DECLS -typedef struct _SysprofDocumentCounterValue +typedef struct _SysprofDocumentTimedValue { gint64 time; union { @@ -35,7 +35,7 @@ typedef struct _SysprofDocumentCounterValue double v_double; guint8 v_raw[8]; }; -} SysprofDocumentCounterValue; +} SysprofDocumentTimedValue; void _sysprof_document_new_async (GMappedFile *mapped_file, GCancellable *cancellable, diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index 93bf6769..665069dd 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -479,7 +479,7 @@ sysprof_document_load_counters (SysprofDocument *self) for (guint j = 0; j < n_counters; j++) { - g_autoptr(GArray) values = g_array_new (FALSE, FALSE, sizeof (SysprofDocumentCounterValue)); + g_autoptr(GArray) values = g_array_new (FALSE, FALSE, sizeof (SysprofDocumentTimedValue)); const char *category; const char *name; const char *description; @@ -520,7 +520,7 @@ sysprof_document_load_counters (SysprofDocument *self) { g_autoptr(SysprofDocumentCtrset) ctrset = g_list_model_get_item (model, i); guint n_values = sysprof_document_ctrset_get_n_values (ctrset); - SysprofDocumentCounterValue ctrval; + SysprofDocumentTimedValue ctrval; ctrval.time = sysprof_document_frame_get_time (SYSPROF_DOCUMENT_FRAME (ctrset)); diff --git a/src/libsysprof-analyze/tests/test-list-counters.c b/src/libsysprof-analyze/tests/test-list-counters.c index fa54b1ae..e5061a7f 100644 --- a/src/libsysprof-analyze/tests/test-list-counters.c +++ b/src/libsysprof-analyze/tests/test-list-counters.c @@ -22,16 +22,31 @@ #include "sysprof-document-private.h" +static gboolean show_values; +static const GOptionEntry entries[] = { + { "values", 'v', 0, G_OPTION_ARG_NONE, &show_values, "Show values along with counter information" }, + { 0 } +}; + int main (int argc, char *argv[]) { + g_autoptr(GOptionContext) context = g_option_context_new ("- list counter information from capture"); g_autoptr(SysprofDocumentLoader) loader = NULL; g_autoptr(SysprofDocument) document = NULL; g_autoptr(GListModel) model = NULL; g_autoptr(GError) error = NULL; guint n_items; + g_option_context_add_main_entries (context, entries, NULL); + + if (!g_option_context_parse (context, &argc, &argv, &error)) + { + g_printerr ("%s\n", error->message); + return 1; + } + if (argc < 2) { g_printerr ("usage: %s CAPTURE_FILE\n", argv[0]); @@ -60,6 +75,32 @@ main (int argc, sysprof_document_counter_get_category (counter), sysprof_document_counter_get_name (counter), sysprof_document_counter_get_description (counter)); + + if (show_values) + { + guint n_values = sysprof_document_counter_get_n_values (counter); + + if (sysprof_document_counter_get_value_type (counter) == G_TYPE_INT64) + { + for (guint j = 0; j < n_values; j++) + { + gint64 t; + gint64 v = sysprof_document_counter_get_value_int64 (counter, j, &t); + + g_print (" %03u: %"G_GINT64_FORMAT": %"G_GINT64_FORMAT"\n", j, t, v); + } + } + else if (sysprof_document_counter_get_value_type (counter) == G_TYPE_DOUBLE) + { + for (guint j = 0; j < n_values; j++) + { + gint64 t; + double v = sysprof_document_counter_get_value_int64 (counter, j, &t); + + g_print (" %03u: %"G_GINT64_FORMAT": %lf\n", j, t, v); + } + } + } } return 0; From 816d0bf33fd22752182dfe69dd5da01c9cd9a02e Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 22 May 2023 16:51:31 -0700 Subject: [PATCH 0203/1030] libsysprof-analyze: warn in invalid type --- src/libsysprof-analyze/sysprof-document-counter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libsysprof-analyze/sysprof-document-counter.c b/src/libsysprof-analyze/sysprof-document-counter.c index e6b31a5a..031a2a17 100644 --- a/src/libsysprof-analyze/sysprof-document-counter.c +++ b/src/libsysprof-analyze/sysprof-document-counter.c @@ -228,7 +228,7 @@ sysprof_document_counter_get_value_type (SysprofDocumentCounter *self) if (self->type == SYSPROF_CAPTURE_COUNTER_DOUBLE) return G_TYPE_DOUBLE; - return G_TYPE_INVALID; + g_return_val_if_reached (G_TYPE_INVALID); } guint From da3e04df0488f53e9df073e5cf7b452bc4212ba5 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 22 May 2023 16:51:44 -0700 Subject: [PATCH 0204/1030] tools: include counter type in dump --- src/tools/sysprof-dump.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/tools/sysprof-dump.c b/src/tools/sysprof-dump.c index 4ca0f2f0..5c12c4b7 100644 --- a/src/tools/sysprof-dump.c +++ b/src/tools/sysprof-dump.c @@ -322,11 +322,12 @@ main (gint argc, SET_CTR_TYPE (ctr->id, ctr->type); - g_print (" COUNTER(%d): %s\n" - " %s\n" - " %s\n" + g_print (" COUNTER(%03d<%s>): %s\n" + " %s\n" + " %s\n" "\n", ctr->id, + ctr->type == SYSPROF_CAPTURE_COUNTER_INT64 ? "i64" : "f64", ctr->category, ctr->name, ctr->description); From 40e9e32030ade512f26f94c93d11f90114844d06 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 22 May 2023 17:09:37 -0700 Subject: [PATCH 0205/1030] libsysprof-analyze: cleanup ctrset value retrieval --- src/libsysprof-analyze/sysprof-document-ctrset.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-document-ctrset.c b/src/libsysprof-analyze/sysprof-document-ctrset.c index de083deb..d0af9fda 100644 --- a/src/libsysprof-analyze/sysprof-document-ctrset.c +++ b/src/libsysprof-analyze/sysprof-document-ctrset.c @@ -76,13 +76,19 @@ sysprof_document_ctrset_get_raw_value (SysprofDocumentCtrset *self, guint8 value[restrict 8]) { const SysprofCaptureCounterSet *ctrset; + guint group; + guint pos; g_return_if_fail (SYSPROF_IS_DOCUMENT_CTRSET (self)); g_return_if_fail (nth < sysprof_document_ctrset_get_n_values (self)); g_return_if_fail (value != NULL); + group = nth / 8; + pos = nth % 8; + ctrset = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureCounterSet); - *id = ctrset->values[nth / 8].ids[nth % 8]; - memcpy (value, &ctrset->values[nth / 8].values[nth % 8], 8); + *id = SYSPROF_DOCUMENT_FRAME_UINT32 (self, ctrset->values[group].ids[pos]); + + memcpy (value, &ctrset->values[group].values[pos], 8); } From 7c31a45f7d376151e6fd51c6727d0012f7fdf30f Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 22 May 2023 17:09:54 -0700 Subject: [PATCH 0206/1030] libsysprof-analyze: include number of values in counter info --- src/libsysprof-analyze/tests/test-list-counters.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libsysprof-analyze/tests/test-list-counters.c b/src/libsysprof-analyze/tests/test-list-counters.c index e5061a7f..64873118 100644 --- a/src/libsysprof-analyze/tests/test-list-counters.c +++ b/src/libsysprof-analyze/tests/test-list-counters.c @@ -69,12 +69,13 @@ main (int argc, { g_autoptr(SysprofDocumentCounter) counter = g_list_model_get_item (model, i); - g_print ("%d<%s> %s.%s: %s\n", + g_print ("%d<%s> %s.%s: %s (%u values)\n", sysprof_document_counter_get_id (counter), sysprof_document_counter_get_value_type (counter) == G_TYPE_INT64 ? "i64" : "f64", sysprof_document_counter_get_category (counter), sysprof_document_counter_get_name (counter), - sysprof_document_counter_get_description (counter)); + sysprof_document_counter_get_description (counter), + sysprof_document_counter_get_n_values (counter)); if (show_values) { From 309f60e263ee086530e436dc650b07f28deda1aa Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 22 May 2023 17:10:10 -0700 Subject: [PATCH 0207/1030] libsysprof-analyze: read double value for counter --- src/libsysprof-analyze/tests/test-list-counters.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libsysprof-analyze/tests/test-list-counters.c b/src/libsysprof-analyze/tests/test-list-counters.c index 64873118..aa9bf112 100644 --- a/src/libsysprof-analyze/tests/test-list-counters.c +++ b/src/libsysprof-analyze/tests/test-list-counters.c @@ -96,7 +96,7 @@ main (int argc, for (guint j = 0; j < n_values; j++) { gint64 t; - double v = sysprof_document_counter_get_value_int64 (counter, j, &t); + double v = sysprof_document_counter_get_value_double (counter, j, &t); g_print (" %03u: %"G_GINT64_FORMAT": %lf\n", j, t, v); } From c2c4495e3ce9e1f057b140189f45e738e5cba830 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 22 May 2023 17:16:48 -0700 Subject: [PATCH 0208/1030] libsysprof-analyze: add --silent option for test This allows testing loading without the print overhead. --- src/libsysprof-analyze/tests/test-symbolize.c | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/libsysprof-analyze/tests/test-symbolize.c b/src/libsysprof-analyze/tests/test-symbolize.c index da98674e..fb7cc71b 100644 --- a/src/libsysprof-analyze/tests/test-symbolize.c +++ b/src/libsysprof-analyze/tests/test-symbolize.c @@ -5,6 +5,14 @@ #include "sysprof-document-private.h" static GMainLoop *main_loop; +static gboolean silent; +static gboolean no_bundled; +static const GOptionEntry entries[] = { + { "no-bundled", 'b', 0, G_OPTION_ARG_NONE, &no_bundled, "Ignore symbols bundled in capture file" }, + { "silent", 's', 0, G_OPTION_ARG_NONE, &silent, "Do not print symbol information" }, + { 0 } +}; + static void load_cb (GObject *object, @@ -29,6 +37,12 @@ load_cb (GObject *object, traceables = sysprof_document_list_traceables (document); n_items = g_list_model_get_n_items (traceables); + if (silent) + { + g_main_loop_quit (main_loop); + return; + } + str = g_string_new (""); for (guint i = 0; i < n_items; i++) @@ -96,12 +110,6 @@ load_cb (GObject *object, g_main_loop_quit (main_loop); } -static gboolean no_bundled; -static const GOptionEntry entries[] = { - { "no-bundled", 'b', 0, G_OPTION_ARG_NONE, &no_bundled, "Ignore symbols bundled in capture file" }, - { 0 } -}; - int main (int argc, char *argv[]) From 754e5df789f19a23727e0bc64e22c8f2dcabab7b Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 22 May 2023 18:35:35 -0700 Subject: [PATCH 0209/1030] libsysprof: only record process from perf, not threads Otherwise we'll get additional SysprofCaptureProcess frames that relate to the threads instead of just the processes. --- src/libsysprof/sysprof-perf-source.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/libsysprof/sysprof-perf-source.c b/src/libsysprof/sysprof-perf-source.c index 8d669ed5..c8baa36d 100644 --- a/src/libsysprof/sysprof-perf-source.c +++ b/src/libsysprof/sysprof-perf-source.c @@ -318,11 +318,12 @@ sysprof_perf_source_handle_event (SysprofPerfCounterEvent *event, offset += sizeof (GPid) + sizeof (GPid); memcpy (&time, event->comm.comm + offset, sizeof time); - sysprof_capture_writer_add_process (self->writer, - time, - cpu, - event->comm.pid, - event->comm.comm); + if (event->comm.pid == event->comm.tid) + sysprof_capture_writer_add_process (self->writer, + time, + cpu, + event->comm.pid, + event->comm.comm); break; From 1469e8691ae325637e927c6bd3a21b552379c655 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 22 May 2023 18:36:27 -0700 Subject: [PATCH 0210/1030] libsysprof-analyze: treat overlay as SysprofMount This creates a synthesized SysprofMount for the overlays so that we can process them as we walk the other mounts. --- src/libsysprof-analyze/sysprof-document.c | 11 +++- .../sysprof-mount-namespace-private.h | 3 - .../sysprof-mount-namespace.c | 57 ++++++++----------- .../sysprof-mount-private.h | 20 +++++++ src/libsysprof-analyze/sysprof-mount.c | 35 +++++++----- 5 files changed, 75 insertions(+), 51 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index 665069dd..866ddd4e 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -35,6 +35,7 @@ #include "sysprof-document-frame-private.h" #include "sysprof-document-jitmap.h" #include "sysprof-document-mmap.h" +#include "sysprof-document-overlay.h" #include "sysprof-document-process-private.h" #include "sysprof-document-symbols-private.h" #include "sysprof-mount-private.h" @@ -437,7 +438,15 @@ sysprof_document_load_overlays (SysprofDocument *self) SysprofProcessInfo *process_info = _sysprof_document_process_info (self, pid, TRUE); if (process_info != NULL) - sysprof_mount_namespace_add_overlay (process_info->mount_namespace, overlay); + { + const char *mount_point = sysprof_document_overlay_get_destination (overlay); + const char *host_path = sysprof_document_overlay_get_source (overlay); + int layer = sysprof_document_overlay_get_layer (overlay); + g_autoptr(SysprofMount) mount = _sysprof_mount_new_for_overlay (self->strings, mount_point, host_path, layer); + + sysprof_mount_namespace_add_mount (process_info->mount_namespace, + g_steal_pointer (&mount)); + } } while (gtk_bitset_iter_next (&iter, &i)); } diff --git a/src/libsysprof-analyze/sysprof-mount-namespace-private.h b/src/libsysprof-analyze/sysprof-mount-namespace-private.h index 5f0f3d6f..6dd80e32 100644 --- a/src/libsysprof-analyze/sysprof-mount-namespace-private.h +++ b/src/libsysprof-analyze/sysprof-mount-namespace-private.h @@ -24,7 +24,6 @@ #include "sysprof-mount-private.h" #include "sysprof-mount-device-private.h" -#include "sysprof-document-overlay.h" G_BEGIN_DECLS @@ -38,8 +37,6 @@ void sysprof_mount_namespace_add_device (SysprofMountNamespa SysprofMountDevice *mount); void sysprof_mount_namespace_add_mount (SysprofMountNamespace *self, SysprofMount *mount); -void sysprof_mount_namespace_add_overlay (SysprofMountNamespace *self, - SysprofDocumentOverlay *overlay); char **sysprof_mount_namespace_translate (SysprofMountNamespace *self, const char *path); diff --git a/src/libsysprof-analyze/sysprof-mount-namespace.c b/src/libsysprof-analyze/sysprof-mount-namespace.c index a355c3bc..31b73c8c 100644 --- a/src/libsysprof-analyze/sysprof-mount-namespace.c +++ b/src/libsysprof-analyze/sysprof-mount-namespace.c @@ -150,16 +150,6 @@ sysprof_mount_namespace_add_mount (SysprofMountNamespace *self, self->mounts_dirty = TRUE; } -void -sysprof_mount_namespace_add_overlay (SysprofMountNamespace *self, - SysprofDocumentOverlay *overlay) -{ - g_return_if_fail (SYSPROF_IS_MOUNT_NAMESPACE (self)); - g_return_if_fail (SYSPROF_IS_DOCUMENT_OVERLAY (overlay)); - - /* TODO */ -} - static SysprofMountDevice * sysprof_mount_namespace_find_device (SysprofMountNamespace *self, SysprofMount *mount, @@ -196,9 +186,9 @@ sysprof_mount_namespace_find_device (SysprofMountNamespace *self, return NULL; } -static gint -sort_by_length (gconstpointer a, - gconstpointer b) +static int +compare_mount (gconstpointer a, + gconstpointer b) { SysprofMount *mount_a = *(SysprofMount * const *)a; SysprofMount *mount_b = *(SysprofMount * const *)b; @@ -209,8 +199,13 @@ sort_by_length (gconstpointer a, return -1; else if (blen > alen) return 1; - else - return 0; + + if (mount_a->layer < mount_b->layer) + return -1; + else if (mount_a->layer > mount_b->layer) + return 1; + + return 0; } /** @@ -237,9 +232,9 @@ sysprof_mount_namespace_translate (SysprofMountNamespace *self, g_return_val_if_fail (SYSPROF_IS_MOUNT_NAMESPACE (self), NULL); g_return_val_if_fail (file != NULL, NULL); - if (self->mounts_dirty) + if G_UNLIKELY (self->mounts_dirty) { - g_ptr_array_sort (self->mounts, sort_by_length); + g_ptr_array_sort (self->mounts, compare_mount); self->mounts_dirty = FALSE; } @@ -253,25 +248,21 @@ sysprof_mount_namespace_translate (SysprofMountNamespace *self, const char *relative; char *translated; - if (!(relative = _sysprof_mount_get_relative_path (mount, file)) || - !(device = sysprof_mount_namespace_find_device (self, mount, relative))) + if (!(relative = _sysprof_mount_get_relative_path (mount, file))) continue; - device_mount_point = sysprof_mount_device_get_mount_point (device); - translated = g_build_filename (device_mount_point, relative, NULL); + if (!mount->is_overlay) + { + if (!(device = sysprof_mount_namespace_find_device (self, mount, relative))) + continue; - /* TODO: Still a bit to do here, because we need to handle overlays - * still as a SysprofMount. Additionally, we may need to adjust the - * paths a bit more based on subvolume, but I need a system such - * as Silverblue or GNOME OS to test that again to match or improve - * on existing behavior in libsysprof. - */ - - /* TODO: After we've translated to what we'd expect to see on the - * host system, we'll need to translate once again to what we can - * actually access if we're inside a container ourselves, such as - * Toolbox or Flatpak and use /var/run/host or similar. - */ + device_mount_point = sysprof_mount_device_get_mount_point (device); + translated = g_build_filename (device_mount_point, relative, NULL); + } + else + { + translated = g_build_filename (mount->mount_source, relative, NULL); + } g_array_append_val (strv, translated); } diff --git a/src/libsysprof-analyze/sysprof-mount-private.h b/src/libsysprof-analyze/sysprof-mount-private.h index fe965c2c..e0b75dd5 100644 --- a/src/libsysprof-analyze/sysprof-mount-private.h +++ b/src/libsysprof-analyze/sysprof-mount-private.h @@ -25,8 +25,28 @@ G_BEGIN_DECLS +struct _SysprofMount +{ + GObject parent_instance; + int mount_id; + int parent_mount_id; + int device_major; + int device_minor; + GRefString *root; + GRefString *mount_point; + GRefString *mount_source; + GRefString *filesystem_type; + GRefString *superblock_options; + guint is_overlay : 1; + guint layer : 15; +}; + SysprofMount *_sysprof_mount_new_for_mountinfo (SysprofStrings *strings, const char *mountinfo); +SysprofMount *_sysprof_mount_new_for_overlay (SysprofStrings *strings, + const char *mount_point, + const char *host_path, + int layer); const char *_sysprof_mount_get_relative_path (SysprofMount *self, const char *path); diff --git a/src/libsysprof-analyze/sysprof-mount.c b/src/libsysprof-analyze/sysprof-mount.c index 76b18b65..32733dc9 100644 --- a/src/libsysprof-analyze/sysprof-mount.c +++ b/src/libsysprof-analyze/sysprof-mount.c @@ -24,20 +24,6 @@ #include "sysprof-mount-private.h" -struct _SysprofMount -{ - GObject parent_instance; - int mount_id; - int parent_mount_id; - int device_major; - int device_minor; - GRefString *root; - GRefString *mount_point; - GRefString *mount_source; - GRefString *filesystem_type; - GRefString *superblock_options; -}; - enum { PROP_0, PROP_DEVICE_MAJOR, @@ -239,6 +225,27 @@ finish: return g_steal_pointer (&self); } +SysprofMount * +_sysprof_mount_new_for_overlay (SysprofStrings *strings, + const char *mount_point, + const char *host_path, + int layer) +{ + SysprofMount *self; + + g_return_val_if_fail (strings != NULL, NULL); + g_return_val_if_fail (mount_point != NULL, NULL); + g_return_val_if_fail (host_path != NULL, NULL); + + self = g_object_new (SYSPROF_TYPE_MOUNT, NULL); + self->mount_point = sysprof_strings_get (strings, mount_point); + self->root = sysprof_strings_get (strings, "/"); + self->mount_source = sysprof_strings_get (strings, host_path); + self->is_overlay = TRUE; + + return self; +} + int sysprof_mount_get_device_major (SysprofMount *self) { From 47538244f374a35018e315a44cb5e44bd02a53c9 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 22 May 2023 18:55:42 -0700 Subject: [PATCH 0211/1030] libsysprof-analyze: actually walk through paths --- src/libsysprof-analyze/sysprof-elf-loader.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-elf-loader.c b/src/libsysprof-analyze/sysprof-elf-loader.c index cb0d9363..72a9f8d8 100644 --- a/src/libsysprof-analyze/sysprof-elf-loader.c +++ b/src/libsysprof-analyze/sysprof-elf-loader.c @@ -405,8 +405,8 @@ sysprof_elf_loader_load (SysprofElfLoader *self, g_clear_object (&elf); } - - return g_steal_pointer (&elf); + if (elf != NULL) + return g_steal_pointer (&elf); } failure: From 26d81f0737903dbd801af4cfafa39434140f661d Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 22 May 2023 18:55:59 -0700 Subject: [PATCH 0212/1030] libsysprof-analyze: print more elf loading information --- src/libsysprof-analyze/tests/test-elf-loader.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libsysprof-analyze/tests/test-elf-loader.c b/src/libsysprof-analyze/tests/test-elf-loader.c index e1c0c67d..0b0e0c95 100644 --- a/src/libsysprof-analyze/tests/test-elf-loader.c +++ b/src/libsysprof-analyze/tests/test-elf-loader.c @@ -73,9 +73,10 @@ main (int argc, g_autoptr(SysprofElf) elf = sysprof_elf_loader_load (elf_loader, info->mount_namespace, file, build_id, file_inode, &error); if (elf == NULL) - g_print ("%u: %s [unresolved]\n", info->pid, file); + g_print ("%u: %s (build-id %s) (inode %"G_GINT64_FORMAT") => [unresolved]\n", + info->pid, file, build_id ? build_id : "none", file_inode); else - g_print ("%u: %s [%s]\n", info->pid, file, sysprof_elf_get_file (elf)); + g_print ("%u: %s => %s\n", info->pid, file, sysprof_elf_get_file (elf)); g_clear_error (&error); } From 04797587e698d612e2b6060651e0223500736ae1 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 22 May 2023 18:56:30 -0700 Subject: [PATCH 0213/1030] libsysprof-analyze: sort overlays before non-overlays We still need to support mounts that do not include overlay frames, but this fixes some of the cases for captures we already generate. --- src/libsysprof-analyze/sysprof-mount-namespace.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-mount-namespace.c b/src/libsysprof-analyze/sysprof-mount-namespace.c index 31b73c8c..89efd86a 100644 --- a/src/libsysprof-analyze/sysprof-mount-namespace.c +++ b/src/libsysprof-analyze/sysprof-mount-namespace.c @@ -195,6 +195,11 @@ compare_mount (gconstpointer a, gsize alen = strlen (sysprof_mount_get_mount_point (mount_a)); gsize blen = strlen (sysprof_mount_get_mount_point (mount_b)); + if (mount_a->is_overlay && !mount_b->is_overlay) + return -1; + else if (!mount_a->is_overlay && mount_b->is_overlay) + return 1; + if (alen > blen) return -1; else if (blen > alen) From 05d5bc6c7711f70d194cd106875a8df53439e562 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 22 May 2023 19:05:25 -0700 Subject: [PATCH 0214/1030] libsysprof-analyze: fix format to match other use --- src/libsysprof-analyze/sysprof-elf-symbolizer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libsysprof-analyze/sysprof-elf-symbolizer.c b/src/libsysprof-analyze/sysprof-elf-symbolizer.c index 5994219e..48896499 100644 --- a/src/libsysprof-analyze/sysprof-elf-symbolizer.c +++ b/src/libsysprof-analyze/sysprof-elf-symbolizer.c @@ -110,7 +110,7 @@ fallback: * 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", + name = g_strdup_printf ("In File %s+0x%"G_GINT64_MODIFIER"x", sysprof_document_mmap_get_file (map), relative_address); begin_address = address; From 59a6f852a02c8e631586c48cfbf715583a8d0026 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 22 May 2023 19:05:41 -0700 Subject: [PATCH 0215/1030] libsysprof-analyze: always ignore jitmap from elf symbolizer --- src/libsysprof-analyze/sysprof-elf-symbolizer.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-elf-symbolizer.c b/src/libsysprof-analyze/sysprof-elf-symbolizer.c index 48896499..6b99bb55 100644 --- a/src/libsysprof-analyze/sysprof-elf-symbolizer.c +++ b/src/libsysprof-analyze/sysprof-elf-symbolizer.c @@ -68,6 +68,10 @@ sysprof_elf_symbolizer_symbolize (SysprofSymbolizer *symbolizer, context != SYSPROF_ADDRESS_CONTEXT_USER)) return NULL; + /* Always ignore jitmap functions, no matter the ordering */ + if ((address & 0xFFFFFFFF00000000) == 0xE000000000000000) + return NULL; + /* First find out what was mapped at that address */ if (!(map = sysprof_address_layout_lookup (process_info->address_layout, address))) return NULL; From a32e82e70ca64bb38d14d032b17a4fa8400f8ea0 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 22 May 2023 19:24:01 -0700 Subject: [PATCH 0216/1030] libsysprof-analyze: add jitmap symbol cache test --- .../tests/test-symbol-cache.c | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/libsysprof-analyze/tests/test-symbol-cache.c b/src/libsysprof-analyze/tests/test-symbol-cache.c index 02956497..2b4ef463 100644 --- a/src/libsysprof-analyze/tests/test-symbol-cache.c +++ b/src/libsysprof-analyze/tests/test-symbol-cache.c @@ -160,6 +160,38 @@ test_interval_tree (void) g_assert_finalize_object (symbols[i].symbol); } +static void +test_jitmap (void) +{ + SysprofSymbolCache *symbol_cache = sysprof_symbol_cache_new (); + + for (guint i = 1; i <= 10000; i++) + { + SysprofAddress begin = 0xE000000000000000 + i; + g_autofree char *name = g_strdup_printf ("%u", i); + SysprofSymbol *symbol = create_symbol (name, begin, begin+1); + + sysprof_symbol_cache_take (symbol_cache, symbol); + } + + g_assert_null (sysprof_symbol_cache_lookup (symbol_cache, 0)); + g_assert_null (sysprof_symbol_cache_lookup (symbol_cache, 10001)); + + for (guint i = 1; i <= 10000; i++) + { + SysprofAddress begin = 0xE000000000000000 + i; + SysprofSymbol *symbol = sysprof_symbol_cache_lookup (symbol_cache, begin); + g_autofree char *name = g_strdup_printf ("%u", i); + + g_assert_nonnull (symbol); + g_assert_cmpint (begin, ==, symbol->begin_address); + g_assert_cmpint (begin+1, ==, symbol->end_address); + g_assert_cmpstr (name, ==, symbol->name); + } + + g_assert_finalize_object (symbol_cache); +} + int main (int argc, char *argv[]) @@ -167,5 +199,7 @@ main (int argc, g_test_init (&argc, &argv, NULL); g_test_add_func ("/libsysprof-analyze/SysprofSymbolCache/interval-tree", test_interval_tree); + g_test_add_func ("/libsysprof-analyze/SysprofSymbolCache/jitmap", + test_jitmap); return g_test_run (); } From 839695dcd1d948cc8ca8f7caf1f280f738672a63 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 22 May 2023 19:49:27 -0700 Subject: [PATCH 0217/1030] libsysprof-analyze: fix SysprofElf:build-id property --- src/libsysprof-analyze/sysprof-elf.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-elf.c b/src/libsysprof-analyze/sysprof-elf.c index 3321827a..8aae956b 100644 --- a/src/libsysprof-analyze/sysprof-elf.c +++ b/src/libsysprof-analyze/sysprof-elf.c @@ -168,7 +168,6 @@ sysprof_elf_finalize (GObject *object) { SysprofElf *self = (SysprofElf *)object; - g_clear_pointer (&self->build_id, g_free); g_clear_pointer (&self->file, g_free); g_clear_pointer (&self->parser, elf_parser_free); g_clear_object (&self->debug_link_elf); @@ -340,7 +339,7 @@ sysprof_elf_get_build_id (SysprofElf *self) { g_return_val_if_fail (SYSPROF_IS_ELF (self), NULL); - return self->build_id; + return elf_parser_get_build_id (self->parser); } const char * From 5cc55117c366c4a57722fcd53eb7eb86e5294386 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 22 May 2023 19:53:38 -0700 Subject: [PATCH 0218/1030] libsysprof-analyze: check cached ELF for build-id/file-inode Just because it's cached doesn't mean we should just trust it. It could have gotten added to the cache previously even though we didn't match. --- src/libsysprof-analyze/sysprof-elf-loader.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/libsysprof-analyze/sysprof-elf-loader.c b/src/libsysprof-analyze/sysprof-elf-loader.c index 72a9f8d8..03684243 100644 --- a/src/libsysprof-analyze/sysprof-elf-loader.c +++ b/src/libsysprof-analyze/sysprof-elf-loader.c @@ -375,7 +375,11 @@ sysprof_elf_loader_load (SysprofElfLoader *self, if (g_hash_table_lookup_extended (self->cache, path, NULL, (gpointer *)&cached_elf)) { if (cached_elf != NULL) - return g_object_ref (cached_elf); + { + if (sysprof_elf_matches (cached_elf, file_inode, build_id)) + return g_object_ref (cached_elf); + } + continue; } From e38eae7aa710879918a1f6ead1e8dbb95ec9799f Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 22 May 2023 20:06:12 -0700 Subject: [PATCH 0219/1030] libsysprof-analyze: sanitize address ranges That way we are clamped to what the mapping actually provides. This only seems to get messed up with glibc, but not clear why and it's barely off so perhaps it has some special mapping tricks it does. I still notice some non-reproducible runs, but that appears to be related to duplicate address mapping entries for [stack] which we need to clear up by removing the duplicate (short) entry. --- .../sysprof-elf-symbolizer.c | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-elf-symbolizer.c b/src/libsysprof-analyze/sysprof-elf-symbolizer.c index 6b99bb55..425500d7 100644 --- a/src/libsysprof-analyze/sysprof-elf-symbolizer.c +++ b/src/libsysprof-analyze/sysprof-elf-symbolizer.c @@ -54,7 +54,8 @@ sysprof_elf_symbolizer_symbolize (SysprofSymbolizer *symbolizer, g_autofree char *name = NULL; const char *path; const char *build_id; - guint64 start_address; + guint64 map_begin; + guint64 map_end; guint64 relative_address; guint64 begin_address; guint64 end_address; @@ -76,12 +77,18 @@ sysprof_elf_symbolizer_symbolize (SysprofSymbolizer *symbolizer, 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 = file_offset + (address - map_begin); + path = sysprof_document_mmap_get_file (map); build_id = sysprof_document_mmap_get_build_id (map); file_inode = sysprof_document_mmap_get_file_inode (map); - file_offset = sysprof_document_mmap_get_file_offset (map); - start_address = sysprof_document_mmap_get_start_address (map); - relative_address = file_offset + (address - start_address); /* See if we can load an ELF at the path . It will be translated from the * mount namespace into something hopefully we can access. @@ -103,11 +110,19 @@ sysprof_elf_symbolizer_symbolize (SysprofSymbolizer *symbolizer, &end_address))) goto fallback; + /* Sanitize address ranges if we have to. Sometimes that can happen + * for us, but it seems to be limited to glibc. + */ + 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++; + return _sysprof_symbol_new (sysprof_strings_get (strings, name), sysprof_strings_get (strings, sysprof_elf_get_nick (elf)), sysprof_strings_get (strings, path), - start_address + (begin_address - file_offset), - start_address + (end_address - file_offset)); + map_begin + (begin_address - file_offset), + map_begin + (end_address - file_offset)); fallback: /* Fallback, we failed to locate the symbol within a file we can @@ -119,6 +134,7 @@ fallback: relative_address); begin_address = address; end_address = address + 1; + return _sysprof_symbol_new (sysprof_strings_get (strings, name), NULL, NULL, begin_address, end_address); } From 44b1297a1b5a4df385adeae0545cca2b15fe10db Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 22 May 2023 20:52:10 -0700 Subject: [PATCH 0220/1030] libsysprof-analyze: remove duplicates in address layout And use a GPtrArray for them instead of gsequence, as it makes it nicer to remove duplicates using a bitset. --- .../sysprof-address-layout.c | 151 +++++++++++++----- 1 file changed, 108 insertions(+), 43 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-address-layout.c b/src/libsysprof-analyze/sysprof-address-layout.c index 163a862d..1e3784fb 100644 --- a/src/libsysprof-analyze/sysprof-address-layout.c +++ b/src/libsysprof-analyze/sysprof-address-layout.c @@ -21,19 +21,21 @@ #include "config.h" #include +#include #include "sysprof-address-layout-private.h" struct _SysprofAddressLayout { GObject parent_instance; - GSequence *mmaps; + GPtrArray *mmaps; + guint mmaps_dirty : 1; }; static guint sysprof_address_layout_get_n_items (GListModel *model) { - return g_sequence_get_length (SYSPROF_ADDRESS_LAYOUT (model)->mmaps); + return SYSPROF_ADDRESS_LAYOUT (model)->mmaps->len; } static GType @@ -47,12 +49,11 @@ sysprof_address_layout_get_item (GListModel *model, guint position) { SysprofAddressLayout *self = SYSPROF_ADDRESS_LAYOUT (model); - GSequenceIter *iter = g_sequence_get_iter_at_pos (self->mmaps, position); - if (g_sequence_iter_is_end (iter)) + if (position >= self->mmaps->len) return NULL; - return g_object_ref (g_sequence_get (iter)); + return g_object_ref (g_ptr_array_index (self->mmaps, position)); } static void @@ -71,7 +72,7 @@ sysprof_address_layout_finalize (GObject *object) { SysprofAddressLayout *self = (SysprofAddressLayout *)object; - g_clear_pointer (&self->mmaps, g_sequence_free); + g_clear_pointer (&self->mmaps, g_ptr_array_unref); G_OBJECT_CLASS (sysprof_address_layout_parent_class)->finalize (object); } @@ -87,7 +88,7 @@ sysprof_address_layout_class_init (SysprofAddressLayoutClass *klass) static void sysprof_address_layout_init (SysprofAddressLayout *self) { - self->mmaps = g_sequence_new (g_object_unref); + self->mmaps = g_ptr_array_new_with_free_func (g_object_unref); } SysprofAddressLayout * @@ -96,25 +97,6 @@ sysprof_address_layout_new (void) return g_object_new (SYSPROF_TYPE_ADDRESS_LAYOUT, NULL); } -static int -sysprof_address_layout_compare (gconstpointer a, - gconstpointer b, - gpointer user_data) -{ - SysprofDocumentMmap *mmap_a = (SysprofDocumentMmap *)a; - SysprofDocumentMmap *mmap_b = (SysprofDocumentMmap *)b; - guint64 begin_a = sysprof_document_mmap_get_start_address (mmap_a); - guint64 begin_b = sysprof_document_mmap_get_start_address (mmap_b); - - if (begin_a < begin_b) - return -1; - - if (begin_a > begin_b) - return 1; - - return 0; -} - void sysprof_address_layout_take (SysprofAddressLayout *self, SysprofDocumentMmap *map) @@ -122,26 +104,86 @@ sysprof_address_layout_take (SysprofAddressLayout *self, g_return_if_fail (SYSPROF_IS_ADDRESS_LAYOUT (self)); g_return_if_fail (SYSPROF_IS_DOCUMENT_MMAP (map)); - g_sequence_insert_sorted (self->mmaps, - map, - sysprof_address_layout_compare, - NULL); + g_ptr_array_add (self->mmaps, map); + + self->mmaps_dirty = TRUE; } static int -sysprof_address_layout_lookup_func (gconstpointer a, - gconstpointer b, - gpointer user_data) +compare_mmaps (gconstpointer a, + gconstpointer b) { - SysprofDocumentMmap *mmap_a = (SysprofDocumentMmap *)a; - const gint64 *addr = b; + SysprofDocumentMmap *mmap_a = *(SysprofDocumentMmap * const *)a; + SysprofDocumentMmap *mmap_b = *(SysprofDocumentMmap * const *)b; + guint64 begin_a = sysprof_document_mmap_get_start_address (mmap_a); + guint64 begin_b = sysprof_document_mmap_get_start_address (mmap_b); + guint64 end_a = sysprof_document_mmap_get_end_address (mmap_a); + guint64 end_b = sysprof_document_mmap_get_end_address (mmap_b); - if (*addr < sysprof_document_mmap_get_start_address (mmap_a)) + if (begin_a < begin_b) + return -1; + + if (begin_a > begin_b) return 1; - if (*addr >= sysprof_document_mmap_get_end_address (mmap_a)) + if (end_a < end_b) return -1; + if (end_a > end_b) + return 1; + + return 0; +} + +static gboolean +mmaps_overlap (SysprofDocumentMmap *a, + SysprofDocumentMmap *b) +{ + if ((sysprof_document_mmap_get_start_address (a) <= sysprof_document_mmap_get_start_address (b)) && + (sysprof_document_mmap_get_end_address (a) > sysprof_document_mmap_get_start_address (b))) + return TRUE; + + return FALSE; +} + +static GtkBitset * +find_duplicates (GPtrArray *sorted) +{ + GtkBitset *bitset = gtk_bitset_new_empty (); + + if (sorted->len == 0) + return bitset; + + for (guint i = 0; i < sorted->len-1; i++) + { + SysprofDocumentMmap *map = g_ptr_array_index (sorted, i); + SysprofDocumentMmap *next = g_ptr_array_index (sorted, i+1); + + /* Take the second one if they overlap, which is generally the large of + * the mmaps. That can happen when something like [stack] is resized into + * a larger mmap. It's useful to remove duplicates so we get more + * predictable bsearch results. + */ + if (mmaps_overlap (map, next)) + gtk_bitset_add (bitset, i); + } + + return bitset; +} + +static int +find_by_address (gconstpointer a, + gconstpointer b) +{ + const SysprofAddress *key = a; + SysprofDocumentMmap *map = *(SysprofDocumentMmap * const *)b; + + if (*key < sysprof_document_mmap_get_start_address (map)) + return -1; + + if (*key >= sysprof_document_mmap_get_end_address (map)) + return 1; + return 0; } @@ -149,14 +191,37 @@ SysprofDocumentMmap * sysprof_address_layout_lookup (SysprofAddressLayout *self, SysprofAddress address) { - GSequenceIter *iter; + SysprofDocumentMmap **ret; g_return_val_if_fail (SYSPROF_IS_ADDRESS_LAYOUT (self), NULL); - iter = g_sequence_lookup (self->mmaps, - &address, - sysprof_address_layout_lookup_func, - NULL); + if (self->mmaps_dirty) + { + g_autoptr(GtkBitset) dups = NULL; + GtkBitsetIter iter; + guint old_len = self->mmaps->len; + guint i; - return iter ? g_sequence_get (iter) : NULL; + self->mmaps_dirty = FALSE; + + g_ptr_array_sort (self->mmaps, compare_mmaps); + dups = find_duplicates (self->mmaps); + + if (gtk_bitset_iter_init_last (&iter, dups, &i)) + { + do + g_ptr_array_remove_index (self->mmaps, i); + while (gtk_bitset_iter_previous (&iter, &i)); + } + + g_list_model_items_changed (G_LIST_MODEL (self), 0, old_len, self->mmaps->len); + } + + ret = bsearch (&address, + self->mmaps->pdata, + self->mmaps->len, + sizeof (gpointer), + find_by_address); + + return ret ? *ret : NULL; } From ab29afd2b1cd959074b55949d7423a976f2390a9 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 22 May 2023 21:13:08 -0700 Subject: [PATCH 0221/1030] libsysprof-analyze: add overlay mountinfo parsing test This just ensures that we can parse the contents of the superblock options that we get for "overlay" mounts via toolbox/podman. These still need to be handled from SysprofMountNamespace when translating though. Additionally, this stuff seems very brittle for parsing, unless it is getting escaped somewhere I'm missing currently. --- src/libsysprof-analyze/tests/meson.build | 1 + .../tests/test-list-overlays.c | 107 ++++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 src/libsysprof-analyze/tests/test-list-overlays.c diff --git a/src/libsysprof-analyze/tests/meson.build b/src/libsysprof-analyze/tests/meson.build index cb859117..9c39724a 100644 --- a/src/libsysprof-analyze/tests/meson.build +++ b/src/libsysprof-analyze/tests/meson.build @@ -17,6 +17,7 @@ libsysprof_analyze_testsuite = { 'test-list-counters' : {'skip': true}, 'test-list-files' : {'skip': true}, 'test-list-jitmap' : {'skip': true}, + 'test-list-overlays' : {'skip': true}, 'test-print-file' : {'skip': true}, 'test-list-processes' : {'skip': true}, 'test-list-address-layout' : {'skip': true}, diff --git a/src/libsysprof-analyze/tests/test-list-overlays.c b/src/libsysprof-analyze/tests/test-list-overlays.c new file mode 100644 index 00000000..e89958e8 --- /dev/null +++ b/src/libsysprof-analyze/tests/test-list-overlays.c @@ -0,0 +1,107 @@ +/* test-list-overlays.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include + +#include "sysprof-document-private.h" + +int +main (int argc, + char *argv[]) +{ + g_autoptr(SysprofDocumentLoader) loader = NULL; + g_autoptr(SysprofDocument) document = NULL; + g_autoptr(GListModel) processes = NULL; + g_autoptr(GError) error = NULL; + guint n_items; + int pid = -1; + + if (argc < 2) + { + g_printerr ("usage: %s CAPTURE_FILE [PID]\n", argv[0]); + return 1; + } + + if (argc == 3) + pid = atoi (argv[2]); + + loader = sysprof_document_loader_new (argv[1]); + sysprof_document_loader_set_symbolizer (loader, sysprof_no_symbolizer_get ()); + + if (!(document = sysprof_document_loader_load (loader, NULL, &error))) + { + g_printerr ("Failed to open capture: %s\n", error->message); + return 1; + } + + processes = sysprof_document_list_processes (document); + n_items = g_list_model_get_n_items (processes); + + for (guint i = 0; i < n_items; i++) + { + g_autoptr(SysprofDocumentProcess) process = g_list_model_get_item (processes, i); + g_autoptr(GListModel) mounts = sysprof_document_process_list_mounts (process); + g_autoptr(GString) str = g_string_new (NULL); + guint n_mounts; + + if (pid != -1 && pid != sysprof_document_frame_get_pid (SYSPROF_DOCUMENT_FRAME (process))) + continue; + + n_mounts = g_list_model_get_n_items (mounts); + for (guint j = 0; j < n_mounts; j++) + { + g_autoptr(SysprofMount) mount = g_list_model_get_item (mounts, j); + const char *fs_type = sysprof_mount_get_filesystem_type (mount); + const char *value; + + if (g_strcmp0 (fs_type, "overlay") == 0) + g_string_append_printf (str, + " @ID(%d) @ROOT(%s) @MOUNT_POINT(%s) @MOUNT_SOURCE(%s) @FS_TYPE(%s) @OPTIONS(%s)\n", + sysprof_mount_get_mount_id (mount), + sysprof_mount_get_root (mount), + sysprof_mount_get_mount_point (mount), + sysprof_mount_get_mount_source (mount), + sysprof_mount_get_filesystem_type (mount), + sysprof_mount_get_superblock_options (mount)); + + if ((value = sysprof_mount_get_superblock_option (mount, "lowerdir"))) + { + g_auto(GStrv) split = g_strsplit (value, ":", 0); + + g_string_append_printf (str, " LOWER: %s\n", value); + for (guint k = 0; split[k]; k++) + g_string_append_printf (str, " [%d]: %s\n", k, split[k]); + } + + if ((value = sysprof_mount_get_superblock_option (mount, "upperdir"))) + g_string_append_printf (str, " UPPER: %s\n", value); + + if ((value = sysprof_mount_get_superblock_option (mount, "workdir"))) + g_string_append_printf (str, " WORKDIR: %s\n", value); + } + + if (str->len > 0) + g_print ("%d:\n%s", + sysprof_document_frame_get_pid (SYSPROF_DOCUMENT_FRAME (process)), + str->str); + } + + return 0; +} From e8eaae3ecad3efc85f7499f2651ca4f3eeff2847 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 23 May 2023 10:24:32 -0700 Subject: [PATCH 0222/1030] libsysprof-analyze: process SysprofElfLoader:debug-dirs This needs to process the debug-dirs as if they're within the mount namespace of the target process. We still need to do external-debug-dirs, but that will be slightly difference as it does not need further translation. --- src/libsysprof-analyze/sysprof-elf-loader.c | 32 ++++++++++++++------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-elf-loader.c b/src/libsysprof-analyze/sysprof-elf-loader.c index 03684243..9311c9cd 100644 --- a/src/libsysprof-analyze/sysprof-elf-loader.c +++ b/src/libsysprof-analyze/sysprof-elf-loader.c @@ -269,23 +269,33 @@ sysprof_elf_loader_annotate (SysprofElfLoader *self, SysprofElf *elf, const char *debug_link) { - g_autoptr(SysprofElf) debug_link_elf = NULL; - g_autofree char *directory_name = NULL; - g_autofree char *debug_path = NULL; - g_autofree char *container_path = NULL; - const char *build_id; - g_assert (SYSPROF_IS_ELF_LOADER (self)); g_assert (SYSPROF_IS_MOUNT_NAMESPACE (mount_namespace)); g_assert (SYSPROF_IS_ELF (elf)); g_assert (debug_link != NULL); - directory_name = g_path_get_dirname (orig_file); - debug_path = g_build_filename ("/usr/lib/debug", directory_name, debug_link, NULL); - build_id = sysprof_elf_get_build_id (elf); + if (self->debug_dirs != NULL) + { + for (guint i = 0; self->debug_dirs[i]; i++) + { + g_autoptr(SysprofElf) debug_link_elf = NULL; + g_autofree char *directory_name = NULL; + g_autofree char *debug_path = NULL; + g_autofree char *container_path = NULL; + const char *debug_dir = self->debug_dirs[i]; + const char *build_id; - if ((debug_link_elf = sysprof_elf_loader_load (self, mount_namespace, debug_path, build_id, 0, NULL))) - sysprof_elf_set_debug_link_elf (elf, get_deepest_debuglink (debug_link_elf)); + directory_name = g_path_get_dirname (orig_file); + debug_path = g_build_filename (debug_dir, directory_name, debug_link, NULL); + build_id = sysprof_elf_get_build_id (elf); + + if ((debug_link_elf = sysprof_elf_loader_load (self, mount_namespace, debug_path, build_id, 0, NULL))) + { + sysprof_elf_set_debug_link_elf (elf, get_deepest_debuglink (debug_link_elf)); + return; + } + } + } } static gboolean From f2a7aa94a2fc2b67e1b9079a7c0ec4133b70a620 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 23 May 2023 10:35:33 -0700 Subject: [PATCH 0223/1030] libsysprof-analyze: add support for external debug dirs The ELF loader can use a set of external debug dirs which will be used to resolve .gnu_debuglink symbols. --- src/libsysprof-analyze/sysprof-elf-loader.c | 39 +++++++++++++++++++-- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-elf-loader.c b/src/libsysprof-analyze/sysprof-elf-loader.c index 9311c9cd..cf25f3fd 100644 --- a/src/libsysprof-analyze/sysprof-elf-loader.c +++ b/src/libsysprof-analyze/sysprof-elf-loader.c @@ -296,6 +296,29 @@ sysprof_elf_loader_annotate (SysprofElfLoader *self, } } } + + if (self->external_debug_dirs != NULL) + { + for (guint i = 0; self->external_debug_dirs[i]; i++) + { + g_autoptr(SysprofElf) debug_link_elf = NULL; + g_autofree char *directory_name = NULL; + g_autofree char *debug_path = NULL; + g_autofree char *container_path = NULL; + const char *debug_dir = self->external_debug_dirs[i]; + const char *build_id; + + directory_name = g_path_get_dirname (orig_file); + debug_path = g_build_filename (debug_dir, directory_name, debug_link, NULL); + build_id = sysprof_elf_get_build_id (elf); + + if ((debug_link_elf = sysprof_elf_loader_load (self, NULL, debug_path, build_id, 0, NULL))) + { + sysprof_elf_set_debug_link_elf (elf, get_deepest_debuglink (debug_link_elf)); + return; + } + } + } } static gboolean @@ -352,16 +375,26 @@ sysprof_elf_loader_load (SysprofElfLoader *self, guint64 file_inode, GError **error) { + const char * const fallback_paths[2] = { file, NULL }; g_auto(GStrv) paths = NULL; g_return_val_if_fail (SYSPROF_IS_ELF_LOADER (self), NULL); - g_return_val_if_fail (SYSPROF_IS_MOUNT_NAMESPACE (mount_namespace), NULL); + g_return_val_if_fail (!mount_namespace || SYSPROF_IS_MOUNT_NAMESPACE (mount_namespace), NULL); /* We must translate the file into a number of paths that may possibly * locate the file in the case that there are overlays in the mount * namespace. Each of the paths could be in a lower overlay layer. + * + * To allow for zero-translation, we allow a NULL mount namespace. + * sysprof_elf_loader_annotate() will use that to load from external + * directories for which on additional translation is necessary. */ - if (!(paths = sysprof_mount_namespace_translate (mount_namespace, file))) + if (mount_namespace == NULL) + paths = g_strdupv ((char **)fallback_paths); + else + paths = sysprof_mount_namespace_translate (mount_namespace, file); + + if (paths == NULL) goto failure; for (guint i = 0; paths[i]; i++) @@ -407,7 +440,7 @@ sysprof_elf_loader_load (SysprofElfLoader *self, if (elf != NULL) { - if ((debug_link = sysprof_elf_get_debug_link (elf))) + if (mount_namespace && (debug_link = sysprof_elf_get_debug_link (elf))) sysprof_elf_loader_annotate (self, mount_namespace, file, elf, debug_link); /* If we loaded the ELF, but it doesn't match what this request is looking From bc482f628b9c2dafb4205b600ee156a3479de7a4 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 23 May 2023 10:44:03 -0700 Subject: [PATCH 0224/1030] libsysprof-analyze: bridge debug dirs to ELF loader --- .../sysprof-elf-symbolizer.c | 125 ++++++++++++++++++ .../sysprof-elf-symbolizer.h | 14 +- 2 files changed, 137 insertions(+), 2 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-elf-symbolizer.c b/src/libsysprof-analyze/sysprof-elf-symbolizer.c index 425500d7..aad691ff 100644 --- a/src/libsysprof-analyze/sysprof-elf-symbolizer.c +++ b/src/libsysprof-analyze/sysprof-elf-symbolizer.c @@ -41,6 +41,15 @@ struct _SysprofElfSymbolizerClass G_DEFINE_FINAL_TYPE (SysprofElfSymbolizer, sysprof_elf_symbolizer, SYSPROF_TYPE_SYMBOLIZER) +enum { + PROP_0, + PROP_DEBUG_DIRS, + PROP_EXTERNAL_DEBUG_DIRS, + N_PROPS +}; + +static GParamSpec *properties [N_PROPS]; + static SysprofSymbol * sysprof_elf_symbolizer_symbolize (SysprofSymbolizer *symbolizer, SysprofStrings *strings, @@ -139,6 +148,22 @@ fallback: NULL, NULL, begin_address, end_address); } +static void +sysprof_elf_symbolizer_loader_notify_cb (SysprofElfSymbolizer *self, + GParamSpec *pspec, + SysprofElfLoader *loader) +{ + g_assert (SYSPROF_IS_ELF_SYMBOLIZER (self)); + g_assert (pspec != NULL); + g_assert (SYSPROF_IS_ELF_LOADER (loader)); + + if (0) {} + else if (g_strcmp0 (pspec->name, "debug-dirs") == 0) + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DEBUG_DIRS]); + else if (g_strcmp0 (pspec->name, "external-debug-dirs") == 0) + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_EXTERNAL_DEBUG_DIRS]); +} + static void sysprof_elf_symbolizer_finalize (GObject *object) { @@ -149,6 +174,52 @@ sysprof_elf_symbolizer_finalize (GObject *object) G_OBJECT_CLASS (sysprof_elf_symbolizer_parent_class)->finalize (object); } +static void +sysprof_elf_symbolizer_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofElfSymbolizer *self = SYSPROF_ELF_SYMBOLIZER (object); + + switch (prop_id) + { + case PROP_DEBUG_DIRS: + g_value_set_boxed (value, sysprof_elf_symbolizer_get_debug_dirs (self)); + break; + + case PROP_EXTERNAL_DEBUG_DIRS: + g_value_set_boxed (value, sysprof_elf_symbolizer_get_external_debug_dirs (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_elf_symbolizer_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SysprofElfSymbolizer *self = SYSPROF_ELF_SYMBOLIZER (object); + + switch (prop_id) + { + case PROP_DEBUG_DIRS: + sysprof_elf_symbolizer_set_debug_dirs (self, g_value_get_boxed (value)); + break; + + case PROP_EXTERNAL_DEBUG_DIRS: + sysprof_elf_symbolizer_set_external_debug_dirs (self, g_value_get_boxed (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + static void sysprof_elf_symbolizer_class_init (SysprofElfSymbolizerClass *klass) { @@ -156,14 +227,34 @@ sysprof_elf_symbolizer_class_init (SysprofElfSymbolizerClass *klass) SysprofSymbolizerClass *symbolizer_class = SYSPROF_SYMBOLIZER_CLASS (klass); object_class->finalize = sysprof_elf_symbolizer_finalize; + object_class->get_property = sysprof_elf_symbolizer_get_property; + object_class->set_property = sysprof_elf_symbolizer_set_property; symbolizer_class->symbolize = sysprof_elf_symbolizer_symbolize; + + properties[PROP_DEBUG_DIRS] = + g_param_spec_boxed ("debug-dirs", NULL, NULL, + G_TYPE_STRV, + (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); + + properties[PROP_EXTERNAL_DEBUG_DIRS] = + g_param_spec_boxed ("external-debug-dirs", NULL, NULL, + G_TYPE_STRV, + (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); } static void sysprof_elf_symbolizer_init (SysprofElfSymbolizer *self) { self->loader = sysprof_elf_loader_new (); + + g_signal_connect_object (self->loader, + "notify", + G_CALLBACK (sysprof_elf_symbolizer_loader_notify_cb), + self, + G_CONNECT_SWAPPED); } SysprofSymbolizer * @@ -171,3 +262,37 @@ sysprof_elf_symbolizer_new (void) { return g_object_new (SYSPROF_TYPE_ELF_SYMBOLIZER, NULL); } + +const char * const * +sysprof_elf_symbolizer_get_debug_dirs (SysprofElfSymbolizer *self) +{ + g_return_val_if_fail (SYSPROF_IS_ELF_SYMBOLIZER (self), NULL); + + return sysprof_elf_loader_get_debug_dirs (self->loader); +} + +void +sysprof_elf_symbolizer_set_debug_dirs (SysprofElfSymbolizer *self, + const char * const *debug_dirs) +{ + g_return_if_fail (SYSPROF_IS_ELF_SYMBOLIZER (self)); + + sysprof_elf_loader_set_debug_dirs (self->loader, debug_dirs); +} + +const char * const * +sysprof_elf_symbolizer_get_external_debug_dirs (SysprofElfSymbolizer *self) +{ + g_return_val_if_fail (SYSPROF_IS_ELF_SYMBOLIZER (self), NULL); + + return sysprof_elf_loader_get_external_debug_dirs (self->loader); +} + +void +sysprof_elf_symbolizer_set_external_debug_dirs (SysprofElfSymbolizer *self, + const char * const *external_debug_dirs) +{ + g_return_if_fail (SYSPROF_IS_ELF_SYMBOLIZER (self)); + + sysprof_elf_loader_set_external_debug_dirs (self->loader, external_debug_dirs); +} diff --git a/src/libsysprof-analyze/sysprof-elf-symbolizer.h b/src/libsysprof-analyze/sysprof-elf-symbolizer.h index 99259f0d..7ea96e55 100644 --- a/src/libsysprof-analyze/sysprof-elf-symbolizer.h +++ b/src/libsysprof-analyze/sysprof-elf-symbolizer.h @@ -33,9 +33,19 @@ typedef struct _SysprofElfSymbolizer SysprofElfSymbolizer; typedef struct _SysprofElfSymbolizerClass SysprofElfSymbolizerClass; SYSPROF_AVAILABLE_IN_ALL -GType sysprof_elf_symbolizer_get_type (void) G_GNUC_CONST; +GType sysprof_elf_symbolizer_get_type (void) G_GNUC_CONST; SYSPROF_AVAILABLE_IN_ALL -SysprofSymbolizer *sysprof_elf_symbolizer_new (void); +SysprofSymbolizer *sysprof_elf_symbolizer_new (void); +SYSPROF_AVAILABLE_IN_ALL +const char * const *sysprof_elf_symbolizer_get_debug_dirs (SysprofElfSymbolizer *self); +SYSPROF_AVAILABLE_IN_ALL +void sysprof_elf_symbolizer_set_debug_dirs (SysprofElfSymbolizer *self, + const char * const *debug_dirs); +SYSPROF_AVAILABLE_IN_ALL +const char * const *sysprof_elf_symbolizer_get_external_debug_dirs (SysprofElfSymbolizer *self); +SYSPROF_AVAILABLE_IN_ALL +void sysprof_elf_symbolizer_set_external_debug_dirs (SysprofElfSymbolizer *self, + const char * const *external_debug_dirs); G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofElfSymbolizer, g_object_unref) From c3f67eb988f0238a93ae99b24ce37b32e626899b Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 23 May 2023 10:47:42 -0700 Subject: [PATCH 0225/1030] libsysprof-analyze: allow specifying external debug dirs from test --- src/libsysprof-analyze/tests/test-symbolize.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/libsysprof-analyze/tests/test-symbolize.c b/src/libsysprof-analyze/tests/test-symbolize.c index fb7cc71b..61dce415 100644 --- a/src/libsysprof-analyze/tests/test-symbolize.c +++ b/src/libsysprof-analyze/tests/test-symbolize.c @@ -7,9 +7,11 @@ static GMainLoop *main_loop; static gboolean silent; static gboolean no_bundled; +static char **debug_dirs; static const GOptionEntry entries[] = { { "no-bundled", 'b', 0, G_OPTION_ARG_NONE, &no_bundled, "Ignore symbols bundled in capture file" }, { "silent", 's', 0, G_OPTION_ARG_NONE, &silent, "Do not print symbol information" }, + { "debug-dir", 'd', 0, G_OPTION_ARG_STRING_ARRAY, &debug_dirs, "Specify external debug directory, may be repeated" }, { 0 } }; @@ -136,12 +138,17 @@ main (int argc, loader = sysprof_document_loader_new (argv[1]); - if (no_bundled) + if (no_bundled || (debug_dirs && g_strv_length (debug_dirs) > 0)) { g_autoptr(SysprofMultiSymbolizer) multi = sysprof_multi_symbolizer_new (); + SysprofSymbolizer *elf = sysprof_elf_symbolizer_new (); + + if (debug_dirs) + sysprof_elf_symbolizer_set_external_debug_dirs (SYSPROF_ELF_SYMBOLIZER (elf), + (const char * const *)debug_dirs); sysprof_multi_symbolizer_take (multi, sysprof_kallsyms_symbolizer_new ()); - sysprof_multi_symbolizer_take (multi, sysprof_elf_symbolizer_new ()); + sysprof_multi_symbolizer_take (multi, elf); sysprof_multi_symbolizer_take (multi, sysprof_jitmap_symbolizer_new ()); sysprof_document_loader_set_symbolizer (loader, SYSPROF_SYMBOLIZER (multi)); From fd69b1051cdf67b5163aaafdd0cde490c2c05d01 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 23 May 2023 10:57:26 -0700 Subject: [PATCH 0226/1030] libsysprof-analyze: add fallback Kernel symbols If we fail to load a /proc/kallsyms.gz from the capture file, we can still provide an "In Kernel+address" symbol. I prefer this to force loading the kallsyms from the current system because that: 1. Requires privileged access and therefore means potentially an authorization dialog. 2. May not even be from the same system that the capture was recorded leading to incorrect symbolization. --- .../sysprof-kallsyms-symbolizer.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-kallsyms-symbolizer.c b/src/libsysprof-analyze/sysprof-kallsyms-symbolizer.c index b910169f..b4464d2e 100644 --- a/src/libsysprof-analyze/sysprof-kallsyms-symbolizer.c +++ b/src/libsysprof-analyze/sysprof-kallsyms-symbolizer.c @@ -251,7 +251,7 @@ sysprof_kallsyms_symbolizer_symbolize (SysprofSymbolizer *symbolizer, return NULL; if (address < self->low || address >= self->high) - return NULL; + goto failure; symbols = &g_array_index (self->kallsyms, KernelSymbol, 0); n_symbols = self->kallsyms->len; @@ -260,7 +260,7 @@ sysprof_kallsyms_symbolizer_symbolize (SysprofSymbolizer *symbolizer, right = n_symbols; mid = n_symbols / 2; - for (;;) + while (left <= right) { const KernelSymbol *ksym = &symbols[mid]; const KernelSymbol *next = &symbols[mid+1]; @@ -281,7 +281,17 @@ sysprof_kallsyms_symbolizer_symbolize (SysprofSymbolizer *symbolizer, mid = left + ((right-left) / 2); } - g_assert_not_reached (); +failure: + { + char name[64]; + + g_snprintf (name, sizeof name, "In Kernel+0x%"G_GINT64_MODIFIER"x", address); + return _sysprof_symbol_new (sysprof_strings_get (strings, name), + g_ref_string_acquire (linux_string), + NULL, + address, + address + 1); + } } static void From b2a48fe38db4ac35642efc066438b76a55f336cc Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 23 May 2023 11:03:34 -0700 Subject: [PATCH 0227/1030] libsysprof-analyze: kallsyms constructor with provided file This allows parsing a kallsyms file that is provided by the application. Such use could be necessary if a capture did not embed /proc/kallsyms.gz. --- .../sysprof-kallsyms-symbolizer.c | 62 +++++++++++++++---- .../sysprof-kallsyms-symbolizer.h | 8 ++- 2 files changed, 55 insertions(+), 15 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-kallsyms-symbolizer.c b/src/libsysprof-analyze/sysprof-kallsyms-symbolizer.c index b4464d2e..d5a9fd7d 100644 --- a/src/libsysprof-analyze/sysprof-kallsyms-symbolizer.c +++ b/src/libsysprof-analyze/sysprof-kallsyms-symbolizer.c @@ -39,6 +39,7 @@ typedef struct _KernelSymbol struct _SysprofKallsymsSymbolizer { SysprofSymbolizer parent_instance; + GInputStream *stream; GArray *kallsyms; guint64 low; guint64 high; @@ -202,22 +203,32 @@ sysprof_kallsyms_symbolizer_prepare_async (SysprofSymbolizer *symbolizer, task = g_task_new (self, cancellable, callback, user_data); g_task_set_source_tag (task, sysprof_kallsyms_symbolizer_prepare_async); - if (!(file = sysprof_document_lookup_file (document, "/proc/kallsyms.gz"))) + if (self->stream == NULL) { - g_task_return_new_error (task, - G_IO_ERROR, - G_IO_ERROR_NOT_SUPPORTED, - "No kallsyms found to decode"); - return; + if (!(file = sysprof_document_lookup_file (document, "/proc/kallsyms.gz"))) + { + g_task_return_new_error (task, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "No kallsyms found to decode"); + return; + } + + decompressor = g_zlib_decompressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP); + input_gz = sysprof_document_file_read (file); + input = g_converter_input_stream_new (input_gz, G_CONVERTER (decompressor)); + + g_task_set_task_data (task, + g_data_input_stream_new (input), + g_object_unref); + } + else + { + g_task_set_task_data (task, + g_steal_pointer (&self->stream), + g_object_unref); } - decompressor = g_zlib_decompressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP); - input_gz = sysprof_document_file_read (file); - input = g_converter_input_stream_new (input_gz, G_CONVERTER (decompressor)); - - g_task_set_task_data (task, - g_data_input_stream_new (input), - g_object_unref); g_task_run_in_thread (task, sysprof_kallsyms_symbolizer_prepare_worker); } @@ -332,3 +343,28 @@ sysprof_kallsyms_symbolizer_new (void) { return g_object_new (SYSPROF_TYPE_KALLSYMS_SYMBOLIZER, NULL); } + +/** + * sysprof_kallsyms_symbolizer_new_for_symbols: + * @symbols: (transfer full): a #GInputStream + * + * Creates a symbolizer using the contents of @symbols as the contents + * of `/proc/kallsyms` for decoding. This is useful if you need to unwind + * symbols from a machine that is different than where the capture was + * recorded and have not embedded `/proc/kallsyms.gz` within the capture + * file for use by #SysprofKallsymsSymbolizer. + * + * Returns: (transfer full): a #SysprofKallsymsSymbolizer + */ +SysprofSymbolizer * +sysprof_kallsyms_symbolizer_new_for_symbols (GInputStream *symbols) +{ + SysprofKallsymsSymbolizer *self; + + g_return_val_if_fail (G_IS_INPUT_STREAM (symbols), NULL); + + self = g_object_new (SYSPROF_TYPE_KALLSYMS_SYMBOLIZER, NULL); + self->stream = symbols; + + return SYSPROF_SYMBOLIZER (self); +} diff --git a/src/libsysprof-analyze/sysprof-kallsyms-symbolizer.h b/src/libsysprof-analyze/sysprof-kallsyms-symbolizer.h index 9a766326..6246b2db 100644 --- a/src/libsysprof-analyze/sysprof-kallsyms-symbolizer.h +++ b/src/libsysprof-analyze/sysprof-kallsyms-symbolizer.h @@ -20,6 +20,8 @@ #pragma once +#include + #include "sysprof-symbolizer.h" G_BEGIN_DECLS @@ -33,9 +35,11 @@ typedef struct _SysprofKallsymsSymbolizer SysprofKallsymsSymbolizer; typedef struct _SysprofKallsymsSymbolizerClass SysprofKallsymsSymbolizerClass; SYSPROF_AVAILABLE_IN_ALL -GType sysprof_kallsyms_symbolizer_get_type (void) G_GNUC_CONST; +GType sysprof_kallsyms_symbolizer_get_type (void) G_GNUC_CONST; SYSPROF_AVAILABLE_IN_ALL -SysprofSymbolizer *sysprof_kallsyms_symbolizer_new (void); +SysprofSymbolizer *sysprof_kallsyms_symbolizer_new (void); +SYSPROF_AVAILABLE_IN_ALL +SysprofSymbolizer *sysprof_kallsyms_symbolizer_new_for_symbols (GInputStream *symbols); G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofKallsymsSymbolizer, g_object_unref) From eb7e4c8224ec753e2274092f25973256759303e6 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 19 May 2023 17:26:02 -0700 Subject: [PATCH 0228/1030] libsysprof-analyze: ensure a GDataInputStream --- src/libsysprof-analyze/sysprof-kallsyms-symbolizer.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libsysprof-analyze/sysprof-kallsyms-symbolizer.c b/src/libsysprof-analyze/sysprof-kallsyms-symbolizer.c index d5a9fd7d..2ba237cc 100644 --- a/src/libsysprof-analyze/sysprof-kallsyms-symbolizer.c +++ b/src/libsysprof-analyze/sysprof-kallsyms-symbolizer.c @@ -364,7 +364,9 @@ sysprof_kallsyms_symbolizer_new_for_symbols (GInputStream *symbols) g_return_val_if_fail (G_IS_INPUT_STREAM (symbols), NULL); self = g_object_new (SYSPROF_TYPE_KALLSYMS_SYMBOLIZER, NULL); - self->stream = symbols; + self->stream = G_INPUT_STREAM (g_data_input_stream_new (symbols)); + + g_object_unref (symbols); return SYSPROF_SYMBOLIZER (self); } From 15acac7e2efe0aa27c6e561ca26557e4d657126e Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 19 May 2023 17:28:11 -0700 Subject: [PATCH 0229/1030] libsysprof-analyze: allow specifying kallsyms path This is useful when opening an older capture which will not have embedded the kallsyms content into the capture. --- src/libsysprof-analyze/tests/test-symbolize.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/libsysprof-analyze/tests/test-symbolize.c b/src/libsysprof-analyze/tests/test-symbolize.c index 61dce415..ec788011 100644 --- a/src/libsysprof-analyze/tests/test-symbolize.c +++ b/src/libsysprof-analyze/tests/test-symbolize.c @@ -8,10 +8,12 @@ static GMainLoop *main_loop; static gboolean silent; static gboolean no_bundled; static char **debug_dirs; +static char *kallsyms_path; static const GOptionEntry entries[] = { { "no-bundled", 'b', 0, G_OPTION_ARG_NONE, &no_bundled, "Ignore symbols bundled in capture file" }, { "silent", 's', 0, G_OPTION_ARG_NONE, &silent, "Do not print symbol information" }, - { "debug-dir", 'd', 0, G_OPTION_ARG_STRING_ARRAY, &debug_dirs, "Specify external debug directory, may be repeated" }, + { "debug-dir", 'd', 0, G_OPTION_ARG_FILENAME_ARRAY, &debug_dirs, "Specify external debug directory, may be repeated" }, + { "kallsyms", 'k', 0, G_OPTION_ARG_FILENAME, &kallsyms_path, "Specify path to kallsyms for kernel symbolizing" }, { 0 } }; @@ -138,16 +140,25 @@ main (int argc, loader = sysprof_document_loader_new (argv[1]); - if (no_bundled || (debug_dirs && g_strv_length (debug_dirs) > 0)) + if (TRUE) { g_autoptr(SysprofMultiSymbolizer) multi = sysprof_multi_symbolizer_new (); SysprofSymbolizer *elf = sysprof_elf_symbolizer_new (); + g_autoptr(GFile) kallsyms_file = kallsyms_path ? g_file_new_for_path (kallsyms_path) : NULL; + GFileInputStream *kallsyms_stream = kallsyms_file ? g_file_read (kallsyms_file, NULL, NULL) : NULL; if (debug_dirs) sysprof_elf_symbolizer_set_external_debug_dirs (SYSPROF_ELF_SYMBOLIZER (elf), (const char * const *)debug_dirs); - sysprof_multi_symbolizer_take (multi, sysprof_kallsyms_symbolizer_new ()); + if (!no_bundled) + sysprof_multi_symbolizer_take (multi, sysprof_bundled_symbolizer_new ()); + + if (kallsyms_stream == NULL) + sysprof_multi_symbolizer_take (multi, sysprof_kallsyms_symbolizer_new ()); + else + sysprof_multi_symbolizer_take (multi, sysprof_kallsyms_symbolizer_new_for_symbols (G_INPUT_STREAM (kallsyms_stream))); + sysprof_multi_symbolizer_take (multi, elf); sysprof_multi_symbolizer_take (multi, sysprof_jitmap_symbolizer_new ()); From b6f3379c2e1fb64b94255320348648b4c9456920 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 19 May 2023 18:47:15 -0700 Subject: [PATCH 0230/1030] libsysprof-analyze: ensure relative path on entry --- src/libsysprof-analyze/sysprof-mount-namespace.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-mount-namespace.c b/src/libsysprof-analyze/sysprof-mount-namespace.c index 89efd86a..7f2c3e73 100644 --- a/src/libsysprof-analyze/sysprof-mount-namespace.c +++ b/src/libsysprof-analyze/sysprof-mount-namespace.c @@ -161,6 +161,9 @@ sysprof_mount_namespace_find_device (SysprofMountNamespace *self, g_assert (SYSPROF_IS_MOUNT_NAMESPACE (self)); g_assert (SYSPROF_IS_MOUNT (mount)); + while (relative_path[0] == '/') + relative_path++; + mount_source = sysprof_mount_get_mount_source (mount); subvolume = sysprof_mount_get_superblock_option (mount, "subvol"); From 5ef6911d65dbb02b29ee00cdbe06c87533a1069b Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 23 May 2023 15:37:57 -0700 Subject: [PATCH 0231/1030] libsysprof-analyze: ignore /sysroot devices I highly doubt this is foolproof, but this makes Silverblue more likely to resolve the proper fstab entry when translating to paths we can access from the host system. --- src/libsysprof-analyze/sysprof-mount-namespace.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-mount-namespace.c b/src/libsysprof-analyze/sysprof-mount-namespace.c index 7f2c3e73..7e146471 100644 --- a/src/libsysprof-analyze/sysprof-mount-namespace.c +++ b/src/libsysprof-analyze/sysprof-mount-namespace.c @@ -178,9 +178,16 @@ sysprof_mount_namespace_find_device (SysprofMountNamespace *self, if (subvolume != NULL) { const char *device_subvolume = sysprof_mount_device_get_subvolume (device); + const char *mount_point = sysprof_mount_device_get_mount_point (device); if (g_strcmp0 (subvolume, device_subvolume) != 0) continue; + + /* Just ignore /sysroot, as it seems to be a convention on systems like + * Silverblue or GNOME OS. + */ + if (g_strcmp0 (mount_point, "/sysroot") == 0) + continue; } return device; From 5bf178e07bdbda0102cbb1a95f514d5de98c015d Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 23 May 2023 15:39:27 -0700 Subject: [PATCH 0232/1030] libsysprof-analyze: include mount root when translating Additionally, if we're on a subvolume, and that subvolume matches the prefix of the root, then skip past that. I have no idea if this is the right thing to do, but it's what we were doing before and seems to be able to help us get proper path resolving on Silverblue. --- .../sysprof-mount-namespace.c | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-mount-namespace.c b/src/libsysprof-analyze/sysprof-mount-namespace.c index 7e146471..52bff189 100644 --- a/src/libsysprof-analyze/sysprof-mount-namespace.c +++ b/src/libsysprof-analyze/sysprof-mount-namespace.c @@ -266,17 +266,31 @@ sysprof_mount_namespace_translate (SysprofMountNamespace *self, if (!(relative = _sysprof_mount_get_relative_path (mount, file))) continue; - if (!mount->is_overlay) + if (mount->is_overlay) { + translated = g_build_filename (mount->mount_source, relative, NULL); + } + else + { + const char *root; + const char *subvolume; + if (!(device = sysprof_mount_namespace_find_device (self, mount, relative))) continue; device_mount_point = sysprof_mount_device_get_mount_point (device); - translated = g_build_filename (device_mount_point, relative, NULL); - } - else - { - translated = g_build_filename (mount->mount_source, relative, NULL); + root = sysprof_mount_get_root (mount); + subvolume = sysprof_mount_device_get_subvolume (device); + + if (root != NULL && subvolume != NULL) + { + if (g_strcmp0 (root, subvolume) == 0) + root = "/"; + else if (g_str_has_prefix (root, subvolume) && root[strlen (subvolume)] == '/') + root += strlen (subvolume); + } + + translated = g_build_filename (device_mount_point, root, relative, NULL); } g_array_append_val (strv, translated); From 4926723228f27ffad176955a582d994b639bfbd4 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 23 May 2023 19:49:21 -0700 Subject: [PATCH 0233/1030] libsysprof-analyze: process lower/upper dir from overlay In modern podman, we will get lowerdir= and upperdir= in the superblock options which we can parse to find where things are on the host. Use that instead of relying on overlay mounts. --- .../sysprof-mount-namespace.c | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-mount-namespace.c b/src/libsysprof-analyze/sysprof-mount-namespace.c index 52bff189..d528d0b0 100644 --- a/src/libsysprof-analyze/sysprof-mount-namespace.c +++ b/src/libsysprof-analyze/sysprof-mount-namespace.c @@ -260,16 +260,46 @@ sysprof_mount_namespace_translate (SysprofMountNamespace *self, SysprofMount *mount = g_ptr_array_index (self->mounts, i); SysprofMountDevice *device; const char *device_mount_point; + const char *fs_type; const char *relative; char *translated; if (!(relative = _sysprof_mount_get_relative_path (mount, file))) continue; + fs_type = sysprof_mount_get_filesystem_type (mount); + if (mount->is_overlay) { translated = g_build_filename (mount->mount_source, relative, NULL); } + else if (g_strcmp0 (fs_type, "overlay") == 0) + { + g_autofree char *lowerdir_str = sysprof_mount_get_superblock_option (mount, "lowerdir"); + g_autofree char *upperdir_str = sysprof_mount_get_superblock_option (mount, "upperdir"); + g_auto(GStrv) lowerdirs = lowerdir_str ? g_strsplit (lowerdir_str, ":", 0) : NULL; + g_auto(GStrv) upperdirs = upperdir_str ? g_strsplit (upperdir_str, ":", 0) : NULL; + + if (upperdirs != NULL) + { + for (guint j = 0; upperdirs[j]; j++) + { + translated = g_build_filename (upperdirs[j], relative, NULL); + g_array_append_val (strv, translated); + } + } + + if (lowerdirs != NULL) + { + for (guint j = 0; lowerdirs[j]; j++) + { + translated = g_build_filename (lowerdirs[j], relative, NULL); + g_array_append_val (strv, translated); + } + } + + continue; + } else { const char *root; From 059aec5a1e1c3f8ffa9ec51341e8e6c20feca827 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 24 May 2023 11:50:14 -0700 Subject: [PATCH 0234/1030] libsysprof-analyze: try to load .build-id debug paths If we have a build-id, we might find it in a path like the following: /usr/lib/debug/.build-id/aa/aa123456789012345678901234567890 so do that lookup before we try to resolve the debuglink name. --- src/libsysprof-analyze/sysprof-elf-loader.c | 33 +++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-elf-loader.c b/src/libsysprof-analyze/sysprof-elf-loader.c index cf25f3fd..31f02545 100644 --- a/src/libsysprof-analyze/sysprof-elf-loader.c +++ b/src/libsysprof-analyze/sysprof-elf-loader.c @@ -262,6 +262,33 @@ get_deepest_debuglink (SysprofElf *elf) return debug_link ? get_deepest_debuglink (debug_link) : elf; } +static gboolean +try_load_build_id (SysprofElfLoader *self, + SysprofMountNamespace *mount_namespace, + SysprofElf *elf, + const char *build_id, + const char *debug_dir) +{ + g_assert (SYSPROF_IS_ELF_LOADER (self)); + g_assert (!mount_namespace || SYSPROF_IS_MOUNT_NAMESPACE (mount_namespace)); + g_assert (SYSPROF_IS_ELF (elf)); + + if (build_id && build_id[0] && build_id[1]) + { + char prefix[3] = {build_id[0], build_id[1], 0}; + g_autofree char *build_id_path = g_build_filename (debug_dir, ".build-id", prefix, build_id, NULL); + g_autoptr(SysprofElf) debug_link_elf = sysprof_elf_loader_load (self, mount_namespace, build_id_path, build_id, 0, NULL); + + if (debug_link_elf != NULL) + { + sysprof_elf_set_debug_link_elf (elf, debug_link_elf); + return TRUE; + } + } + + return FALSE; +} + static void sysprof_elf_loader_annotate (SysprofElfLoader *self, SysprofMountNamespace *mount_namespace, @@ -289,6 +316,9 @@ sysprof_elf_loader_annotate (SysprofElfLoader *self, debug_path = g_build_filename (debug_dir, directory_name, debug_link, NULL); build_id = sysprof_elf_get_build_id (elf); + if (try_load_build_id (self, mount_namespace, elf, build_id, debug_dir)) + return; + if ((debug_link_elf = sysprof_elf_loader_load (self, mount_namespace, debug_path, build_id, 0, NULL))) { sysprof_elf_set_debug_link_elf (elf, get_deepest_debuglink (debug_link_elf)); @@ -312,6 +342,9 @@ sysprof_elf_loader_annotate (SysprofElfLoader *self, debug_path = g_build_filename (debug_dir, directory_name, debug_link, NULL); build_id = sysprof_elf_get_build_id (elf); + if (try_load_build_id (self, NULL, elf, build_id, debug_dir)) + return; + if ((debug_link_elf = sysprof_elf_loader_load (self, NULL, debug_path, build_id, 0, NULL))) { sysprof_elf_set_debug_link_elf (elf, get_deepest_debuglink (debug_link_elf)); From f7882d5c5f6f9a45f16fff41b1640e41bf4a915c Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 24 May 2023 12:44:26 -0700 Subject: [PATCH 0235/1030] libsysprof-analyze: add vfunc for getting stack addresses This allows us to avoid the function call overhead for each of the instruction pointers in the stack trace. Instead let the interface handle the decoding of the whole set. --- .../sysprof-document-allocation.c | 19 +++++++++++++++++-- .../sysprof-document-sample.c | 19 +++++++++++++++++-- .../sysprof-document-traceable.c | 10 +--------- .../sysprof-document-traceable.h | 9 ++++++--- 4 files changed, 41 insertions(+), 16 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-document-allocation.c b/src/libsysprof-analyze/sysprof-document-allocation.c index 0a287339..773bc7f2 100644 --- a/src/libsysprof-analyze/sysprof-document-allocation.c +++ b/src/libsysprof-analyze/sysprof-document-allocation.c @@ -59,7 +59,21 @@ sysprof_document_allocation_get_stack_address (SysprofDocumentTraceable *traceab { const SysprofCaptureAllocation *allocation = SYSPROF_DOCUMENT_FRAME_GET (traceable, SysprofCaptureAllocation); - return SYSPROF_DOCUMENT_FRAME_UINT64 (traceable, allocation->addrs[position]); + return SYSPROF_DOCUMENT_FRAME_UINT16 (traceable, allocation->addrs[position]); +} + +static guint +sysprof_document_allocation_get_stack_addresses (SysprofDocumentTraceable *traceable, + guint64 *addresses, + guint n_addresses) +{ + const SysprofCaptureAllocation *allocation = SYSPROF_DOCUMENT_FRAME_GET (traceable, SysprofCaptureAllocation); + guint depth = MIN (n_addresses, SYSPROF_DOCUMENT_FRAME_UINT16 (traceable, allocation->n_addrs)); + + for (guint i = 0; i < depth; i++) + addresses[i] = SYSPROF_DOCUMENT_FRAME_UINT64 (traceable, allocation->addrs[i]); + + return depth; } static void @@ -67,6 +81,7 @@ traceable_iface_init (SysprofDocumentTraceableInterface *iface) { iface->get_stack_depth = sysprof_document_allocation_get_stack_depth; iface->get_stack_address = sysprof_document_allocation_get_stack_address; + iface->get_stack_addresses = sysprof_document_allocation_get_stack_addresses; } G_DEFINE_FINAL_TYPE_WITH_CODE (SysprofDocumentAllocation, sysprof_document_allocation, SYSPROF_TYPE_DOCUMENT_FRAME, @@ -119,7 +134,7 @@ sysprof_document_allocation_class_init (SysprofDocumentAllocationClass *klass) /** * SysprofDocumentAllocation:tid: * - * The task-id or thread-id of the thread which was sampled. + * The task-id or thread-id of the thread which was traced. * * On Linux, this is generally set to the value of the gettid() syscall. * diff --git a/src/libsysprof-analyze/sysprof-document-sample.c b/src/libsysprof-analyze/sysprof-document-sample.c index 685668d7..73ba990c 100644 --- a/src/libsysprof-analyze/sysprof-document-sample.c +++ b/src/libsysprof-analyze/sysprof-document-sample.c @@ -52,11 +52,25 @@ sysprof_document_sample_get_stack_depth (SysprofDocumentTraceable *traceable) static guint64 sysprof_document_sample_get_stack_address (SysprofDocumentTraceable *traceable, - guint position) + guint position) { const SysprofCaptureSample *sample = SYSPROF_DOCUMENT_FRAME_GET (traceable, SysprofCaptureSample); - return SYSPROF_DOCUMENT_FRAME_UINT64 (traceable, sample->addrs[position]); + return SYSPROF_DOCUMENT_FRAME_UINT16 (traceable, sample->addrs[position]); +} + +static guint +sysprof_document_sample_get_stack_addresses (SysprofDocumentTraceable *traceable, + guint64 *addresses, + guint n_addresses) +{ + const SysprofCaptureSample *sample = SYSPROF_DOCUMENT_FRAME_GET (traceable, SysprofCaptureSample); + guint depth = MIN (n_addresses, SYSPROF_DOCUMENT_FRAME_UINT16 (traceable, sample->n_addrs)); + + for (guint i = 0; i < depth; i++) + addresses[i] = SYSPROF_DOCUMENT_FRAME_UINT64 (traceable, sample->addrs[i]); + + return depth; } static void @@ -64,6 +78,7 @@ traceable_iface_init (SysprofDocumentTraceableInterface *iface) { iface->get_stack_depth = sysprof_document_sample_get_stack_depth; iface->get_stack_address = sysprof_document_sample_get_stack_address; + iface->get_stack_addresses = sysprof_document_sample_get_stack_addresses; } G_DEFINE_FINAL_TYPE_WITH_CODE (SysprofDocumentSample, sysprof_document_sample, SYSPROF_TYPE_DOCUMENT_FRAME, diff --git a/src/libsysprof-analyze/sysprof-document-traceable.c b/src/libsysprof-analyze/sysprof-document-traceable.c index 8bf4f250..620bc0cc 100644 --- a/src/libsysprof-analyze/sysprof-document-traceable.c +++ b/src/libsysprof-analyze/sysprof-document-traceable.c @@ -63,18 +63,10 @@ sysprof_document_traceable_get_stack_addresses (SysprofDocumentTraceable *self, guint64 *addresses, guint n_addresses) { - guint64 (*decoder) (SysprofDocumentTraceable *, guint); - g_return_val_if_fail (SYSPROF_IS_DOCUMENT_TRACEABLE (self), 0); if (addresses == NULL || n_addresses == 0) return 0; - decoder = SYSPROF_DOCUMENT_TRACEABLE_GET_IFACE (self)->get_stack_address; - n_addresses = MIN (n_addresses, sysprof_document_traceable_get_stack_depth (self)); - - for (guint i = 0; i < n_addresses; i++) - addresses[i] = decoder (self, i); - - return n_addresses; + return SYSPROF_DOCUMENT_TRACEABLE_GET_IFACE (self)->get_stack_addresses (self, addresses, n_addresses); } diff --git a/src/libsysprof-analyze/sysprof-document-traceable.h b/src/libsysprof-analyze/sysprof-document-traceable.h index 904ac65e..459990ba 100644 --- a/src/libsysprof-analyze/sysprof-document-traceable.h +++ b/src/libsysprof-analyze/sysprof-document-traceable.h @@ -34,9 +34,12 @@ struct _SysprofDocumentTraceableInterface { GTypeInterface parent; - guint (*get_stack_depth) (SysprofDocumentTraceable *self); - guint64 (*get_stack_address) (SysprofDocumentTraceable *self, - guint position); + guint (*get_stack_depth) (SysprofDocumentTraceable *self); + guint64 (*get_stack_address) (SysprofDocumentTraceable *self, + guint position); + guint (*get_stack_addresses) (SysprofDocumentTraceable *self, + guint64 *addresses, + guint n_addresses); }; SYSPROF_AVAILABLE_IN_ALL From b68d8225ecf7a41ddc3083557efa164a82b0fe39 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 24 May 2023 12:53:34 -0700 Subject: [PATCH 0236/1030] libsysprof-analyze: print the counter ids that were set --- .../tests/test-capture-model.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/libsysprof-analyze/tests/test-capture-model.c b/src/libsysprof-analyze/tests/test-capture-model.c index ac94872f..d9356adc 100644 --- a/src/libsysprof-analyze/tests/test-capture-model.c +++ b/src/libsysprof-analyze/tests/test-capture-model.c @@ -103,6 +103,24 @@ main (int argc, category, name); } } + else if (SYSPROF_IS_DOCUMENT_CTRSET (frame)) + { + guint n_values = sysprof_document_ctrset_get_n_values (SYSPROF_DOCUMENT_CTRSET (frame)); + + g_print (" counters=["); + for (guint j = 0; j < n_values; j++) + { + guint id; + guint8 raw[8]; + + sysprof_document_ctrset_get_raw_value (SYSPROF_DOCUMENT_CTRSET (frame), j, &id, raw); + + g_print ("%u", id); + if (j+1 != n_values) + g_print (", "); + } + g_print ("]"); + } else if (SYSPROF_IS_DOCUMENT_ALLOCATION (frame)) { if (sysprof_document_allocation_is_free (SYSPROF_DOCUMENT_ALLOCATION (frame))) From 8348f7fe2c327c5b5f28095612e5b4137c379c75 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 24 May 2023 13:00:47 -0700 Subject: [PATCH 0237/1030] libsysprof-analyze: start on scaffolding for callgraph API --- src/libsysprof-analyze/meson.build | 4 + src/libsysprof-analyze/sysprof-analyze.h | 2 + .../sysprof-callgraph-frame.c | 84 +++++++ .../sysprof-callgraph-frame.h | 34 +++ .../sysprof-callgraph-private.h | 38 +++ src/libsysprof-analyze/sysprof-callgraph.c | 221 ++++++++++++++++++ src/libsysprof-analyze/sysprof-callgraph.h | 34 +++ src/libsysprof-analyze/sysprof-document.c | 75 ++++++ src/libsysprof-analyze/sysprof-document.h | 11 + 9 files changed, 503 insertions(+) create mode 100644 src/libsysprof-analyze/sysprof-callgraph-frame.c create mode 100644 src/libsysprof-analyze/sysprof-callgraph-frame.h create mode 100644 src/libsysprof-analyze/sysprof-callgraph-private.h create mode 100644 src/libsysprof-analyze/sysprof-callgraph.c create mode 100644 src/libsysprof-analyze/sysprof-callgraph.h diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build index 00e3f408..d8d25a12 100644 --- a/src/libsysprof-analyze/meson.build +++ b/src/libsysprof-analyze/meson.build @@ -1,5 +1,7 @@ libsysprof_analyze_public_sources = [ 'sysprof-bundled-symbolizer.c', + 'sysprof-callgraph.c', + 'sysprof-callgraph-frame.c', 'sysprof-document.c', 'sysprof-document-allocation.c', 'sysprof-document-counter.c', @@ -49,6 +51,8 @@ libsysprof_analyze_private_sources = [ libsysprof_analyze_public_headers = [ 'sysprof-analyze.h', + 'sysprof-callgraph.h', + 'sysprof-callgraph-frame.h', 'sysprof-bundled-symbolizer.h', 'sysprof-document.h', 'sysprof-document-allocation.h', diff --git a/src/libsysprof-analyze/sysprof-analyze.h b/src/libsysprof-analyze/sysprof-analyze.h index 2962fc67..4eda88a9 100644 --- a/src/libsysprof-analyze/sysprof-analyze.h +++ b/src/libsysprof-analyze/sysprof-analyze.h @@ -26,6 +26,8 @@ G_BEGIN_DECLS #define SYSPROF_ANALYZE_INSIDE # include "sysprof-bundled-symbolizer.h" +# include "sysprof-callgraph.h" +# include "sysprof-callgraph-frame.h" # include "sysprof-document.h" # include "sysprof-document-allocation.h" # include "sysprof-document-counter.h" diff --git a/src/libsysprof-analyze/sysprof-callgraph-frame.c b/src/libsysprof-analyze/sysprof-callgraph-frame.c new file mode 100644 index 00000000..67ba2bee --- /dev/null +++ b/src/libsysprof-analyze/sysprof-callgraph-frame.c @@ -0,0 +1,84 @@ +/* sysprof-callgraph-frame.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-callgraph-frame.h" + +struct _SysprofCallgraphFrame +{ + GObject parent_instance; +}; + +enum { + PROP_0, + N_PROPS +}; + +G_DEFINE_FINAL_TYPE (SysprofCallgraphFrame, sysprof_callgraph_frame, G_TYPE_OBJECT) + +static GParamSpec *properties [N_PROPS]; + +static void +sysprof_callgraph_frame_finalize (GObject *object) +{ + G_OBJECT_CLASS (sysprof_callgraph_frame_parent_class)->finalize (object); +} + +static void +sysprof_callgraph_frame_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + switch (prop_id) + { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_callgraph_frame_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (prop_id) + { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_callgraph_frame_class_init (SysprofCallgraphFrameClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = sysprof_callgraph_frame_finalize; + object_class->get_property = sysprof_callgraph_frame_get_property; + object_class->set_property = sysprof_callgraph_frame_set_property; +} + +static void +sysprof_callgraph_frame_init (SysprofCallgraphFrame *self) +{ +} diff --git a/src/libsysprof-analyze/sysprof-callgraph-frame.h b/src/libsysprof-analyze/sysprof-callgraph-frame.h new file mode 100644 index 00000000..2c650fd7 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-callgraph-frame.h @@ -0,0 +1,34 @@ +/* sysprof-callgraph-frame.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +#include + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_CALLGRAPH_FRAME (sysprof_callgraph_frame_get_type()) + +SYSPROF_AVAILABLE_IN_ALL +G_DECLARE_FINAL_TYPE (SysprofCallgraphFrame, sysprof_callgraph_frame, SYSPROF, CALLGRAPH_FRAME, GObject) + +G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-callgraph-private.h b/src/libsysprof-analyze/sysprof-callgraph-private.h new file mode 100644 index 00000000..e1bd247b --- /dev/null +++ b/src/libsysprof-analyze/sysprof-callgraph-private.h @@ -0,0 +1,38 @@ +/* sysprof-callgraph-private.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +#include "sysprof-callgraph.h" +#include "sysprof-document.h" + +G_BEGIN_DECLS + +void _sysprof_callgraph_new_async (SysprofDocument *document, + GListModel *traceables, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +SysprofCallgraph *_sysprof_callgraph_new_finish (GAsyncResult *result, + GError **error); + +G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-callgraph.c b/src/libsysprof-analyze/sysprof-callgraph.c new file mode 100644 index 00000000..79918052 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-callgraph.c @@ -0,0 +1,221 @@ +/* sysprof-callgraph.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-callgraph-private.h" +#include "sysprof-callgraph-frame.h" +#include "sysprof-document-traceable.h" + +#define MAX_STACK_DEPTH 1024 + +struct _SysprofCallgraph +{ + GObject parent_instance; + SysprofDocument *document; + GListModel *traceables; +}; + +typedef struct _SysprofCallgraphNode +{ + SysprofSymbol *symbol; + + struct _SysprofCallgraphNode *next; + struct _SysprofCallgraphNode *prev; + + struct _SysprofCallgraphNode *parent; + struct _SysprofCallgraphNode *children; +} SysprofCallgraphNode; + +typedef struct _SysprofCallgraphTrace +{ + guint model_position; + guint n_nodes; + SysprofCallgraphNode nodes[]; +} SysprofCallgraphTrace; + +static GType +sysprof_callgraph_get_item_type (GListModel *model) +{ + return SYSPROF_TYPE_CALLGRAPH_FRAME; +} + +static guint +sysprof_callgraph_get_n_items (GListModel *model) +{ + return 0; +} + +static gpointer +sysprof_callgraph_get_item (GListModel *model, + guint position) +{ + return NULL; +} + +static void +list_model_iface_init (GListModelInterface *iface) +{ + iface->get_item_type = sysprof_callgraph_get_item_type; + iface->get_n_items = sysprof_callgraph_get_n_items; + iface->get_item = sysprof_callgraph_get_item; +} + +G_DEFINE_FINAL_TYPE_WITH_CODE (SysprofCallgraph, sysprof_callgraph, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init)) + +static void +sysprof_callgraph_finalize (GObject *object) +{ + SysprofCallgraph *self = (SysprofCallgraph *)object; + + g_clear_object (&self->document); + g_clear_object (&self->traceables); + + G_OBJECT_CLASS (sysprof_callgraph_parent_class)->finalize (object); +} + +static void +sysprof_callgraph_class_init (SysprofCallgraphClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = sysprof_callgraph_finalize; +} + +static void +sysprof_callgraph_init (SysprofCallgraph *self) +{ +} + +static void +sysprof_callgraph_trace_free (SysprofCallgraphTrace *trace) +{ + g_free (trace); +} + +static void +sysprof_callgraph_add_trace (SysprofCallgraph *self, + SysprofCallgraphTrace *trace) +{ + g_assert (SYSPROF_IS_CALLGRAPH (self)); + g_assert (trace != NULL); + + sysprof_callgraph_trace_free (trace); +} + +static void +sysprof_callgraph_add_traceable (SysprofCallgraph *self, + SysprofDocumentTraceable *traceable, + guint model_position) +{ + SysprofCallgraphTrace *trace; + SysprofSymbol **symbols; + guint stack_depth; + guint n_symbols; + + g_assert (SYSPROF_IS_CALLGRAPH (self)); + g_assert (SYSPROF_IS_DOCUMENT_TRACEABLE (traceable)); + + stack_depth = sysprof_document_traceable_get_stack_depth (traceable); + + if (stack_depth > MAX_STACK_DEPTH) + return; + + symbols = g_newa (SysprofSymbol *, stack_depth); + n_symbols = sysprof_document_symbolize_traceable (self->document, traceable, symbols, stack_depth); + + if (n_symbols == 0) + return; + + trace = g_malloc (sizeof *trace + (n_symbols * sizeof (SysprofCallgraphNode))); + trace->model_position = model_position; + trace->n_nodes = n_symbols; + + for (guint i = 0; i < n_symbols; i++) + { + trace->nodes[i].symbol = symbols[i]; + trace->nodes[i].children = NULL; + trace->nodes[i].parent = NULL; + trace->nodes[i].next = NULL; + trace->nodes[i].prev = NULL; + } + + sysprof_callgraph_add_trace (self, trace); +} + +static void +sysprof_callgraph_new_worker (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) +{ + SysprofCallgraph *self = task_data; + guint n_items; + + g_assert (G_IS_TASK (task)); + g_assert (source_object == NULL); + g_assert (SYSPROF_IS_CALLGRAPH (self)); + g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); + + n_items = g_list_model_get_n_items (self->traceables); + + for (guint i = 0; i < n_items; i++) + { + g_autoptr(SysprofDocumentTraceable) traceable = g_list_model_get_item (self->traceables, i); + + sysprof_callgraph_add_traceable (self, traceable, i); + } + + g_task_return_pointer (task, g_object_ref (self), g_object_unref); +} + +void +_sysprof_callgraph_new_async (SysprofDocument *document, + GListModel *traceables, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_autoptr(SysprofCallgraph) self = NULL; + g_autoptr(GTask) task = NULL; + + g_return_if_fail (SYSPROF_IS_DOCUMENT (document)); + g_return_if_fail (G_IS_LIST_MODEL (traceables)); + g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); + + self = g_object_new (SYSPROF_TYPE_CALLGRAPH, NULL); + self->document = g_object_ref (document); + self->traceables = g_object_ref (traceables); + + task = g_task_new (NULL, cancellable, callback, user_data); + g_task_set_source_tag (task, _sysprof_callgraph_new_async); + g_task_set_task_data (task, g_object_ref (self), g_object_unref); + g_task_run_in_thread (task, sysprof_callgraph_new_worker); +} + +SysprofCallgraph * +_sysprof_callgraph_new_finish (GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (G_IS_TASK (result), NULL); + + return g_task_propagate_pointer (G_TASK (result), error); +} diff --git a/src/libsysprof-analyze/sysprof-callgraph.h b/src/libsysprof-analyze/sysprof-callgraph.h new file mode 100644 index 00000000..2391758a --- /dev/null +++ b/src/libsysprof-analyze/sysprof-callgraph.h @@ -0,0 +1,34 @@ +/* sysprof-callgraph.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +#include + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_CALLGRAPH (sysprof_callgraph_get_type()) + +SYSPROF_AVAILABLE_IN_ALL +G_DECLARE_FINAL_TYPE (SysprofCallgraph, sysprof_callgraph, SYSPROF, CALLGRAPH, GObject) + +G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index 866ddd4e..64e76ea5 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -26,6 +26,7 @@ #include "sysprof-document-private.h" +#include "sysprof-callgraph-private.h" #include "sysprof-document-bitset-index-private.h" #include "sysprof-document-counter-private.h" #include "sysprof-document-ctrdef.h" @@ -1056,3 +1057,77 @@ sysprof_document_list_counters (SysprofDocument *self) return g_object_ref (G_LIST_MODEL (self->counters)); } + +static void +sysprof_document_callgraph_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + g_autoptr(SysprofCallgraph) callgraph = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GTask) task = user_data; + + g_assert (G_IS_ASYNC_RESULT (result)); + g_assert (G_IS_TASK (task)); + + if (!(callgraph = _sysprof_callgraph_new_finish (result, &error))) + g_task_return_error (task, g_steal_pointer (&error)); + else + g_task_return_pointer (task, g_steal_pointer (&callgraph), g_object_unref); +} + +/** + * sysprof_document_callgraph_async: + * @self: a #SysprofDocument + * @traceables: a list model of traceables for the callgraph + * @cancellable: (nullable): a #GCancellable or %NULL + * @callback: a callback to execute upon completion + * @user_data: closure data for @callback + * + * Asynchronously generates a callgraph using the @traceables to get + * the call stacks. + */ +void +sysprof_document_callgraph_async (SysprofDocument *self, + GListModel *traceables, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_autoptr(GTask) task = NULL; + + g_return_if_fail (SYSPROF_IS_DOCUMENT (self)); + g_return_if_fail (G_IS_LIST_MODEL (traceables)); + g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); + + task = g_task_new (self, cancellable, callback, user_data); + g_task_set_source_tag (task, sysprof_document_callgraph_async); + + _sysprof_callgraph_new_async (self, + traceables, + cancellable, + sysprof_document_callgraph_cb, + g_steal_pointer (&task)); +} + +/** + * sysprof_document_callgraph_finish: + * @self: a #SysprofDocument + * @result: the #GAsyncResult provided to callback + * @error: a location for a #GError + * + * Completes a request to generate a callgraph. + * + * Returns: (transfer full): a #SysprofCallgraph if successful; otherwise %NULL + * and @error is set. + */ +SysprofCallgraph * +sysprof_document_callgraph_finish (SysprofDocument *self, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (SYSPROF_IS_DOCUMENT (self), NULL); + g_return_val_if_fail (G_IS_TASK (result), NULL); + + return g_task_propagate_pointer (G_TASK (result), error); +} diff --git a/src/libsysprof-analyze/sysprof-document.h b/src/libsysprof-analyze/sysprof-document.h index df2371e7..26cd392c 100644 --- a/src/libsysprof-analyze/sysprof-document.h +++ b/src/libsysprof-analyze/sysprof-document.h @@ -24,6 +24,7 @@ #include +#include "sysprof-callgraph.h" #include "sysprof-document-file.h" #include "sysprof-document-traceable.h" #include "sysprof-symbol.h" @@ -53,5 +54,15 @@ guint sysprof_document_symbolize_traceable (SysprofDocument SysprofDocumentTraceable *traceable, SysprofSymbol **symbols, guint n_symbols); +SYSPROF_AVAILABLE_IN_ALL +void sysprof_document_callgraph_async (SysprofDocument *self, + GListModel *traceables, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +SYSPROF_AVAILABLE_IN_ALL +SysprofCallgraph *sysprof_document_callgraph_finish (SysprofDocument *self, + GAsyncResult *result, + GError **error); G_END_DECLS From 5b00127d7de8a3c107f475373b12dbaf2f56dbc3 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 24 May 2023 14:30:13 -0700 Subject: [PATCH 0238/1030] libsysprof-analyze: add API to get only samples We'll probably want this for allocations too at some point. --- src/libsysprof-analyze/sysprof-document.c | 26 ++++++++++++++++++++++- src/libsysprof-analyze/sysprof-document.h | 2 ++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index 64e76ea5..99095e9b 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -62,6 +62,7 @@ struct _SysprofDocument SysprofStrings *strings; GtkBitset *file_chunks; + GtkBitset *samples; GtkBitset *traceables; GtkBitset *processes; GtkBitset *mmaps; @@ -216,6 +217,7 @@ sysprof_document_finalize (GObject *object) g_clear_pointer (&self->overlays, gtk_bitset_unref); g_clear_pointer (&self->pids, gtk_bitset_unref); g_clear_pointer (&self->processes, gtk_bitset_unref); + g_clear_pointer (&self->samples, gtk_bitset_unref); g_clear_pointer (&self->traceables, gtk_bitset_unref); g_clear_object (&self->counters); @@ -255,6 +257,7 @@ sysprof_document_init (SysprofDocument *self) self->overlays = gtk_bitset_new_empty (); self->pids = gtk_bitset_new_empty (); self->processes = gtk_bitset_new_empty (); + self->samples = gtk_bitset_new_empty (); self->traceables = gtk_bitset_new_empty (); self->files_first_position = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); @@ -628,7 +631,11 @@ sysprof_document_load_worker (GTask *task, switch ((int)tainted->type) { case SYSPROF_CAPTURE_FRAME_ALLOCATION: + gtk_bitset_add (self->traceables, self->frames->len); + break; + case SYSPROF_CAPTURE_FRAME_SAMPLE: + gtk_bitset_add (self->samples, self->frames->len); gtk_bitset_add (self->traceables, self->frames->len); break; @@ -950,7 +957,7 @@ sysprof_document_list_files (SysprofDocument *self) * Gets a #GListModel containing #SysprofDocumentTraceable found within * the #SysprofDocument. * - * Returns: (transfer full): a #GListModel of #SysprofTraceable + * Returns: (transfer full): a #GListModel of #SysprofDocumentTraceable */ GListModel * sysprof_document_list_traceables (SysprofDocument *self) @@ -960,6 +967,23 @@ sysprof_document_list_traceables (SysprofDocument *self) return _sysprof_document_bitset_index_new (G_LIST_MODEL (self), self->traceables); } +/** + * sysprof_document_list_samples: + * @self: a #SysprofDocument + * + * Gets a #GListModel containing #SysprofDocumentSample found within + * the #SysprofDocument. + * + * Returns: (transfer full): a #GListModel of #SysprofDocumentSample + */ +GListModel * +sysprof_document_list_samples (SysprofDocument *self) +{ + g_return_val_if_fail (SYSPROF_IS_DOCUMENT (self), NULL); + + return _sysprof_document_bitset_index_new (G_LIST_MODEL (self), self->samples); +} + /** * sysprof_document_list_processes: * @self: a #SysprofDocument diff --git a/src/libsysprof-analyze/sysprof-document.h b/src/libsysprof-analyze/sysprof-document.h index 26cd392c..6413065e 100644 --- a/src/libsysprof-analyze/sysprof-document.h +++ b/src/libsysprof-analyze/sysprof-document.h @@ -44,6 +44,8 @@ GListModel *sysprof_document_list_files (SysprofDocument SYSPROF_AVAILABLE_IN_ALL GListModel *sysprof_document_list_traceables (SysprofDocument *self); SYSPROF_AVAILABLE_IN_ALL +GListModel *sysprof_document_list_samples (SysprofDocument *self); +SYSPROF_AVAILABLE_IN_ALL GListModel *sysprof_document_list_processes (SysprofDocument *self); SYSPROF_AVAILABLE_IN_ALL GListModel *sysprof_document_list_jitmaps (SysprofDocument *self); From f12f2b760cfa36adcc377d003a09f35fd650b553 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 24 May 2023 15:19:44 -0700 Subject: [PATCH 0239/1030] libsysprof-analyze: add API to get symbol to represent process And include a fallback in case we never got an actual Process frame which will contain the cmdline for the process. We need to hold onto the fallback too so that we can keep symbols lightweight by not having to reference them so long as the document is alive. --- .../sysprof-document-private.h | 3 ++ src/libsysprof-analyze/sysprof-document.c | 52 +++++++++++++++++++ .../sysprof-process-info-private.h | 2 + src/libsysprof-analyze/sysprof-process-info.c | 7 +++ 4 files changed, 64 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-document-private.h b/src/libsysprof-analyze/sysprof-document-private.h index 5f93ba07..3a84384e 100644 --- a/src/libsysprof-analyze/sysprof-document-private.h +++ b/src/libsysprof-analyze/sysprof-document-private.h @@ -24,6 +24,7 @@ #include "sysprof-document.h" #include "sysprof-symbolizer.h" +#include "sysprof-symbol.h" G_BEGIN_DECLS @@ -55,5 +56,7 @@ gboolean _sysprof_document_is_native (SysprofDocument *self) GRefString *_sysprof_document_ref_string (SysprofDocument *self, const char *name); GtkBitset *_sysprof_document_traceables (SysprofDocument *self); +SysprofSymbol *_sysprof_document_process_symbol (SysprofDocument *self, + int pid); G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index 99095e9b..8e49daee 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -44,6 +44,7 @@ #include "sysprof-mount-namespace-private.h" #include "sysprof-process-info-private.h" #include "sysprof-strings-private.h" +#include "sysprof-symbol-private.h" #include "sysprof-symbolizer-private.h" #include "line-reader-private.h" @@ -456,6 +457,40 @@ sysprof_document_load_overlays (SysprofDocument *self) } } +static void +sysprof_document_load_processes (SysprofDocument *self) +{ + GtkBitsetIter iter; + guint i; + + g_assert (SYSPROF_IS_DOCUMENT (self)); + + if (gtk_bitset_iter_init_first (&iter, self->processes, &i)) + { + do + { + g_autoptr(SysprofDocumentProcess) process = g_list_model_get_item (G_LIST_MODEL (self), i); + int pid = sysprof_document_frame_get_pid (SYSPROF_DOCUMENT_FRAME (process)); + SysprofProcessInfo *process_info = _sysprof_document_process_info (self, pid, TRUE); + const char *cmdline = sysprof_document_process_get_command_line (process); + + if (cmdline != NULL) + { + g_auto(GStrv) parts = NULL; + + if ((parts = g_strsplit (cmdline , " ", 2))) + { + g_clear_object (&process_info->symbol); + process_info->symbol = + _sysprof_symbol_new (sysprof_strings_get (self->strings, parts[0]), + NULL, NULL, 0, 0); + } + } + } + while (gtk_bitset_iter_next (&iter, &i)); + } +} + static void sysprof_document_load_counters (SysprofDocument *self) { @@ -690,6 +725,7 @@ sysprof_document_load_worker (GTask *task, sysprof_document_load_mounts (self); sysprof_document_load_mountinfos (self); sysprof_document_load_memory_maps (self); + sysprof_document_load_processes (self); sysprof_document_load_overlays (self); sysprof_document_load_counters (self); @@ -1155,3 +1191,19 @@ sysprof_document_callgraph_finish (SysprofDocument *self, return g_task_propagate_pointer (G_TASK (result), error); } + +SysprofSymbol * +_sysprof_document_process_symbol (SysprofDocument *self, + int pid) +{ + SysprofProcessInfo *info; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT (self), NULL); + + info = _sysprof_document_process_info (self, pid, TRUE); + + if (info->symbol) + return info->symbol; + + return info->fallback_symbol; +} diff --git a/src/libsysprof-analyze/sysprof-process-info-private.h b/src/libsysprof-analyze/sysprof-process-info-private.h index 2f9cc4a8..ca0647de 100644 --- a/src/libsysprof-analyze/sysprof-process-info-private.h +++ b/src/libsysprof-analyze/sysprof-process-info-private.h @@ -31,6 +31,8 @@ typedef struct _SysprofProcessInfo SysprofAddressLayout *address_layout; SysprofMountNamespace *mount_namespace; SysprofSymbolCache *symbol_cache; + SysprofSymbol *fallback_symbol; + SysprofSymbol *symbol; int pid; } SysprofProcessInfo; diff --git a/src/libsysprof-analyze/sysprof-process-info.c b/src/libsysprof-analyze/sysprof-process-info.c index 5977d5a4..e0346175 100644 --- a/src/libsysprof-analyze/sysprof-process-info.c +++ b/src/libsysprof-analyze/sysprof-process-info.c @@ -21,6 +21,7 @@ #include "config.h" #include "sysprof-process-info-private.h" +#include "sysprof-symbol-private.h" G_DEFINE_BOXED_TYPE (SysprofProcessInfo, sysprof_process_info, @@ -32,12 +33,16 @@ sysprof_process_info_new (SysprofMountNamespace *mount_namespace, int pid) { SysprofProcessInfo *self; + char symname[32]; + + g_snprintf (symname, sizeof symname, "[Process %d]", pid); self = g_atomic_rc_box_new0 (SysprofProcessInfo); self->pid = pid; self->address_layout = sysprof_address_layout_new (); self->symbol_cache = sysprof_symbol_cache_new (); self->mount_namespace = mount_namespace; + self->fallback_symbol = _sysprof_symbol_new (g_ref_string_new (symname), NULL, NULL, 0, 0); return self; } @@ -56,6 +61,8 @@ sysprof_process_info_finalize (gpointer data) g_clear_object (&self->address_layout); g_clear_object (&self->symbol_cache); g_clear_object (&self->mount_namespace); + g_clear_object (&self->fallback_symbol); + g_clear_object (&self->symbol); self->pid = 0; } From d723e41683cddb5320630dbc33a8e92bcc3f5a39 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 24 May 2023 17:07:52 -0700 Subject: [PATCH 0240/1030] libsysprof-analyze: wrap processes in [] like previously --- src/libsysprof-analyze/sysprof-document.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index 8e49daee..c79e4f65 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -480,9 +480,11 @@ sysprof_document_load_processes (SysprofDocument *self) if ((parts = g_strsplit (cmdline , " ", 2))) { + g_autofree char *wrapped = g_strdup_printf ("[%s]", parts[0]); + g_clear_object (&process_info->symbol); process_info->symbol = - _sysprof_symbol_new (sysprof_strings_get (self->strings, parts[0]), + _sysprof_symbol_new (sysprof_strings_get (self->strings, wrapped), NULL, NULL, 0, 0); } } From ca2b008547ac11670f98c02364fbc57642869e3d Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 24 May 2023 17:08:09 -0700 Subject: [PATCH 0241/1030] libsysprof-analyze: skip symbols we failed to symbolize --- src/libsysprof-analyze/sysprof-document.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index c79e4f65..b30354e0 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -1080,6 +1080,7 @@ sysprof_document_symbolize_traceable (SysprofDocument *self, const SysprofProcessInfo *process_info; SysprofAddress *addresses; guint n_addresses; + guint n_symbolized = 0; int pid; g_return_val_if_fail (SYSPROF_IS_DOCUMENT (self), 0); @@ -1097,13 +1098,16 @@ sysprof_document_symbolize_traceable (SysprofDocument *self, { SysprofAddressContext context; - symbols[i] = _sysprof_document_symbols_lookup (self->symbols, process_info, last_context, addresses[i]); + symbols[n_symbolized] = _sysprof_document_symbols_lookup (self->symbols, process_info, last_context, addresses[i]); + + if (symbols[n_symbolized] != NULL) + n_symbolized++; if (sysprof_address_is_context_switch (addresses[i], &context)) last_context = context; } - return n_addresses; + return n_symbolized; } /** From 5e9f745f3821db21585067ca20bcd12b72a0dc71 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 24 May 2023 17:08:48 -0700 Subject: [PATCH 0242/1030] libsysprof-analyze: pre-hash symbols for faster comparisons --- src/libsysprof-analyze/sysprof-symbol-private.h | 15 +++++++++++++++ src/libsysprof-analyze/sysprof-symbol.c | 8 ++++++++ 2 files changed, 23 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-symbol-private.h b/src/libsysprof-analyze/sysprof-symbol-private.h index 1af67980..b859155e 100644 --- a/src/libsysprof-analyze/sysprof-symbol-private.h +++ b/src/libsysprof-analyze/sysprof-symbol-private.h @@ -29,6 +29,8 @@ struct _SysprofSymbol { GObject parent_instance; + guint hash; + GRefString *name; GRefString *binary_path; GRefString *binary_nick; @@ -43,4 +45,17 @@ SysprofSymbol *_sysprof_symbol_new (GRefString *name, SysprofAddress begin_address, SysprofAddress end_address); +static inline gboolean +_sysprof_symbol_equal (const SysprofSymbol *a, + const SysprofSymbol *b) +{ + if (a == b) + return TRUE; + + if (a->hash != b->hash) + return FALSE; + + return strcmp (a->name, b->name) == 0; +} + G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-symbol.c b/src/libsysprof-analyze/sysprof-symbol.c index 719ffb85..1d44209e 100644 --- a/src/libsysprof-analyze/sysprof-symbol.c +++ b/src/libsysprof-analyze/sysprof-symbol.c @@ -144,6 +144,14 @@ _sysprof_symbol_new (GRefString *name, self->binary_nick = binary_nick; self->begin_address = begin_address; self->end_address = end_address; + self->hash = g_str_hash (name); return self; } + +gboolean +sysprof_symbol_equal (const SysprofSymbol *a, + const SysprofSymbol *b) +{ + return _sysprof_symbol_equal (a, b); +} From 3ae108464dce3e5f0b4ed11d914cf953f57719d4 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 24 May 2023 17:11:30 -0700 Subject: [PATCH 0243/1030] libsysprof-analyze: implement augmented callgraph This creates a SysprofCallgraph object which is a GListModel of SysprofCallgraphFrame. The SysprofCallgraphFrame is also a GListModel of SysprofCallgraphFrame so that we can map this all into a GtkListView in the future for tree-like visibility. The augmentation allows for the same callgraph code to be used for multiple scenarios such as CPU sampling as well as memory allocation tracking. If your augmentation size is <=sizeof(void*) then you do not occur an extra allocation and you can use the inline augmentation space. The test-callgraph clearly shows that we still need to do the shuffling of -- Kernel -- and -- User -- like the old callgraph code did. But that will come soon enough. --- .../sysprof-callgraph-frame-private.h | 31 +++ .../sysprof-callgraph-frame.c | 147 +++++++++++-- .../sysprof-callgraph-frame.h | 7 + .../sysprof-callgraph-private.h | 28 ++- src/libsysprof-analyze/sysprof-callgraph.c | 202 +++++++++++++----- src/libsysprof-analyze/sysprof-callgraph.h | 30 +++ src/libsysprof-analyze/sysprof-document.c | 30 ++- src/libsysprof-analyze/sysprof-document.h | 14 +- src/libsysprof-analyze/tests/meson.build | 1 + src/libsysprof-analyze/tests/test-callgraph.c | 154 +++++++++++++ 10 files changed, 552 insertions(+), 92 deletions(-) create mode 100644 src/libsysprof-analyze/sysprof-callgraph-frame-private.h create mode 100644 src/libsysprof-analyze/tests/test-callgraph.c diff --git a/src/libsysprof-analyze/sysprof-callgraph-frame-private.h b/src/libsysprof-analyze/sysprof-callgraph-frame-private.h new file mode 100644 index 00000000..a33c68db --- /dev/null +++ b/src/libsysprof-analyze/sysprof-callgraph-frame-private.h @@ -0,0 +1,31 @@ +/* sysprof-callgraph-frame-private.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-callgraph.h" +#include "sysprof-callgraph-frame.h" + +G_BEGIN_DECLS + +SysprofCallgraphFrame *_sysprof_callgraph_frame_new (SysprofCallgraph *callgraph, + SysprofCallgraphNode *node); + +G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-callgraph-frame.c b/src/libsysprof-analyze/sysprof-callgraph-frame.c index 67ba2bee..b98a94d4 100644 --- a/src/libsysprof-analyze/sysprof-callgraph-frame.c +++ b/src/libsysprof-analyze/sysprof-callgraph-frame.c @@ -20,25 +20,82 @@ #include "config.h" -#include "sysprof-callgraph-frame.h" +#include + +#include "sysprof-callgraph-private.h" +#include "sysprof-callgraph-frame-private.h" struct _SysprofCallgraphFrame { - GObject parent_instance; + GObject parent_instance; + SysprofCallgraph *callgraph; + SysprofCallgraphNode *node; + guint n_children; }; enum { PROP_0, + PROP_SYMBOL, N_PROPS }; -G_DEFINE_FINAL_TYPE (SysprofCallgraphFrame, sysprof_callgraph_frame, G_TYPE_OBJECT) +static GType +sysprof_callgraph_frame_get_item_type (GListModel *model) +{ + return SYSPROF_TYPE_CALLGRAPH_FRAME; +} + +static guint +sysprof_callgraph_frame_get_n_items (GListModel *model) +{ + return SYSPROF_CALLGRAPH_FRAME (model)->n_children; +} + +static gpointer +sysprof_callgraph_frame_get_item (GListModel *model, + guint position) +{ + SysprofCallgraphFrame *self = SYSPROF_CALLGRAPH_FRAME (model); + SysprofCallgraphNode *iter; + + if (self->callgraph == NULL) + return NULL; + + iter = self->node->children; + + while (iter != NULL && position > 0) + { + iter = iter->next; + position--; + } + + if (iter == NULL) + return NULL; + + return _sysprof_callgraph_frame_new (self->callgraph, iter); +} + +static void +list_model_iface_init (GListModelInterface *iface) +{ + iface->get_item_type = sysprof_callgraph_frame_get_item_type; + iface->get_n_items = sysprof_callgraph_frame_get_n_items; + iface->get_item = sysprof_callgraph_frame_get_item; +} + +G_DEFINE_FINAL_TYPE_WITH_CODE (SysprofCallgraphFrame, sysprof_callgraph_frame, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init)) static GParamSpec *properties [N_PROPS]; static void sysprof_callgraph_frame_finalize (GObject *object) { + SysprofCallgraphFrame *self = (SysprofCallgraphFrame *)object; + + g_clear_weak_pointer (&self->callgraph); + self->node = NULL; + G_OBJECT_CLASS (sysprof_callgraph_frame_parent_class)->finalize (object); } @@ -48,21 +105,14 @@ sysprof_callgraph_frame_get_property (GObject *object, GValue *value, GParamSpec *pspec) { - switch (prop_id) - { - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} + SysprofCallgraphFrame *self = SYSPROF_CALLGRAPH_FRAME (object); -static void -sysprof_callgraph_frame_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ switch (prop_id) { + case PROP_SYMBOL: + g_value_set_object (value, sysprof_callgraph_frame_get_symbol (self)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } @@ -75,10 +125,75 @@ sysprof_callgraph_frame_class_init (SysprofCallgraphFrameClass *klass) object_class->finalize = sysprof_callgraph_frame_finalize; object_class->get_property = sysprof_callgraph_frame_get_property; - object_class->set_property = sysprof_callgraph_frame_set_property; + + properties [PROP_SYMBOL] = + g_param_spec_object ("symbol", NULL, NULL, + SYSPROF_TYPE_SYMBOL, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); } static void sysprof_callgraph_frame_init (SysprofCallgraphFrame *self) { } + +SysprofCallgraphFrame * +_sysprof_callgraph_frame_new (SysprofCallgraph *callgraph, + SysprofCallgraphNode *node) +{ + SysprofCallgraphFrame *self; + + g_return_val_if_fail (SYSPROF_IS_CALLGRAPH (callgraph), NULL); + g_return_val_if_fail (node != NULL, NULL); + + self = g_object_new (SYSPROF_TYPE_CALLGRAPH_FRAME, NULL); + g_set_weak_pointer (&self->callgraph, callgraph); + self->node = node; + + for (const SysprofCallgraphNode *iter = node->children; + iter != NULL; + iter = iter->next) + self->n_children++; + + return self; +} + +/** + * sysprof_callgraph_frame_get_symbol: + * @self: a #SysprofCallgraphFrame + * + * Gets the symbol for the frame. + * + * Returns: (nullable) (transfer none): a #SysprofSymbol + */ +SysprofSymbol * +sysprof_callgraph_frame_get_symbol (SysprofCallgraphFrame *self) +{ + g_return_val_if_fail (SYSPROF_IS_CALLGRAPH_FRAME (self), NULL); + + if (self->callgraph == NULL) + return NULL; + + return self->node->symbol; +} + +/** + * sysprof_callgraph_frame_get_augment: (skip) + * @self: a #SysprofCallgraphFrame + * + * Gets the augmentation that was attached to the callgrpah node. + * + * Returns: (nullable) (transfer none): the augmentation data + */ +gpointer +sysprof_callgraph_frame_get_augment (SysprofCallgraphFrame *self) +{ + g_return_val_if_fail (SYSPROF_IS_CALLGRAPH_FRAME (self), NULL); + + if (self->callgraph == NULL) + return NULL; + + return sysprof_callgraph_get_augment (self->callgraph, self->node); +} diff --git a/src/libsysprof-analyze/sysprof-callgraph-frame.h b/src/libsysprof-analyze/sysprof-callgraph-frame.h index 2c650fd7..97b0c6b0 100644 --- a/src/libsysprof-analyze/sysprof-callgraph-frame.h +++ b/src/libsysprof-analyze/sysprof-callgraph-frame.h @@ -24,6 +24,8 @@ #include +#include "sysprof-symbol.h" + G_BEGIN_DECLS #define SYSPROF_TYPE_CALLGRAPH_FRAME (sysprof_callgraph_frame_get_type()) @@ -31,4 +33,9 @@ G_BEGIN_DECLS SYSPROF_AVAILABLE_IN_ALL G_DECLARE_FINAL_TYPE (SysprofCallgraphFrame, sysprof_callgraph_frame, SYSPROF, CALLGRAPH_FRAME, GObject) +SYSPROF_AVAILABLE_IN_ALL +SysprofSymbol *sysprof_callgraph_frame_get_symbol (SysprofCallgraphFrame *self); +SYSPROF_AVAILABLE_IN_ALL +gpointer sysprof_callgraph_frame_get_augment (SysprofCallgraphFrame *self); + G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-callgraph-private.h b/src/libsysprof-analyze/sysprof-callgraph-private.h index e1bd247b..0765ee2e 100644 --- a/src/libsysprof-analyze/sysprof-callgraph-private.h +++ b/src/libsysprof-analyze/sysprof-callgraph-private.h @@ -27,12 +27,26 @@ G_BEGIN_DECLS -void _sysprof_callgraph_new_async (SysprofDocument *document, - GListModel *traceables, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -SysprofCallgraph *_sysprof_callgraph_new_finish (GAsyncResult *result, - GError **error); +struct _SysprofCallgraphNode +{ + SysprofCallgraphNode *parent; + SysprofCallgraphNode *prev; + SysprofCallgraphNode *next; + SysprofCallgraphNode *children; + SysprofSymbol *symbol; + gpointer augment; +}; + +void _sysprof_callgraph_new_async (SysprofDocument *document, + GListModel *traceables, + gsize augment_size, + SysprofAugmentationFunc augment_func, + gpointer augment_func_data, + GDestroyNotify augment_func_data_destroy, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +SysprofCallgraph *_sysprof_callgraph_new_finish (GAsyncResult *result, + GError **error); G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-callgraph.c b/src/libsysprof-analyze/sysprof-callgraph.c index 79918052..a3bf441f 100644 --- a/src/libsysprof-analyze/sysprof-callgraph.c +++ b/src/libsysprof-analyze/sysprof-callgraph.c @@ -21,36 +21,30 @@ #include "config.h" #include "sysprof-callgraph-private.h" -#include "sysprof-callgraph-frame.h" +#include "sysprof-callgraph-frame-private.h" +#include "sysprof-document-private.h" #include "sysprof-document-traceable.h" +#include "sysprof-symbol-private.h" #define MAX_STACK_DEPTH 1024 struct _SysprofCallgraph { - GObject parent_instance; - SysprofDocument *document; - GListModel *traceables; + GObject parent_instance; + + SysprofDocument *document; + GListModel *traceables; + + SysprofSymbol *everything; + + gsize augment_size; + SysprofAugmentationFunc augment_func; + gpointer augment_func_data; + GDestroyNotify augment_func_data_destroy; + + SysprofCallgraphNode root; }; -typedef struct _SysprofCallgraphNode -{ - SysprofSymbol *symbol; - - struct _SysprofCallgraphNode *next; - struct _SysprofCallgraphNode *prev; - - struct _SysprofCallgraphNode *parent; - struct _SysprofCallgraphNode *children; -} SysprofCallgraphNode; - -typedef struct _SysprofCallgraphTrace -{ - guint model_position; - guint n_nodes; - SysprofCallgraphNode nodes[]; -} SysprofCallgraphTrace; - static GType sysprof_callgraph_get_item_type (GListModel *model) { @@ -60,14 +54,19 @@ sysprof_callgraph_get_item_type (GListModel *model) static guint sysprof_callgraph_get_n_items (GListModel *model) { - return 0; + return 1; } static gpointer sysprof_callgraph_get_item (GListModel *model, guint position) { - return NULL; + SysprofCallgraph *self = SYSPROF_CALLGRAPH (model); + + if (position > 0) + return NULL; + + return _sysprof_callgraph_frame_new (self, &self->root); } static void @@ -81,6 +80,24 @@ list_model_iface_init (GListModelInterface *iface) G_DEFINE_FINAL_TYPE_WITH_CODE (SysprofCallgraph, sysprof_callgraph, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init)) +static void +sysprof_callgraph_dispose (GObject *object) +{ + SysprofCallgraph *self = (SysprofCallgraph *)object; + GDestroyNotify notify = self->augment_func_data_destroy; + gpointer notify_data = self->augment_func_data; + + self->augment_size = 0; + self->augment_func = NULL; + self->augment_func_data = NULL; + self->augment_func_data_destroy = NULL; + + if (notify != NULL) + notify (notify_data); + + G_OBJECT_CLASS (sysprof_callgraph_parent_class)->dispose (object); +} + static void sysprof_callgraph_finalize (GObject *object) { @@ -88,6 +105,7 @@ sysprof_callgraph_finalize (GObject *object) g_clear_object (&self->document); g_clear_object (&self->traceables); + g_clear_object (&self->everything); G_OBJECT_CLASS (sysprof_callgraph_parent_class)->finalize (object); } @@ -97,68 +115,105 @@ sysprof_callgraph_class_init (SysprofCallgraphClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); + object_class->dispose = sysprof_callgraph_dispose; object_class->finalize = sysprof_callgraph_finalize; } static void sysprof_callgraph_init (SysprofCallgraph *self) { + self->everything = _sysprof_symbol_new (g_ref_string_new_intern ("[Everything]"), NULL, NULL, 0, 0); + self->root.symbol = self->everything; } -static void -sysprof_callgraph_trace_free (SysprofCallgraphTrace *trace) +static SysprofCallgraphNode * +sysprof_callgraph_add_trace (SysprofCallgraph *self, + SysprofSymbol **symbols, + guint n_symbols) { - g_free (trace); -} + SysprofCallgraphNode *parent; -static void -sysprof_callgraph_add_trace (SysprofCallgraph *self, - SysprofCallgraphTrace *trace) -{ g_assert (SYSPROF_IS_CALLGRAPH (self)); - g_assert (trace != NULL); + g_assert (n_symbols > 2); + g_assert (symbols[n_symbols-1] == self->everything); - sysprof_callgraph_trace_free (trace); + parent = &self->root; + + for (guint i = n_symbols - 1; i > 0; i--) + { + SysprofSymbol *symbol = symbols[i-1]; + SysprofCallgraphNode *node = NULL; + + /* Try to find @symbol within the children of @parent */ + for (SysprofCallgraphNode *iter = parent->children; + iter != NULL; + iter = iter->next) + { + g_assert (iter != NULL); + g_assert (iter->symbol != NULL); + g_assert (symbol != NULL); + + if (_sysprof_symbol_equal (iter->symbol, symbol)) + { + node = iter; + goto next_symbol; + } + } + + /* Otherwise create a new node */ + node = g_new0 (SysprofCallgraphNode, 1); + node->symbol = symbol; + node->parent = parent; + node->next = parent->children; + if (parent->children) + parent->children->prev = node; + parent->children = node; + + next_symbol: + parent = node; + } + + return parent; } static void sysprof_callgraph_add_traceable (SysprofCallgraph *self, - SysprofDocumentTraceable *traceable, - guint model_position) + SysprofDocumentTraceable *traceable) { - SysprofCallgraphTrace *trace; + SysprofCallgraphNode *node; SysprofSymbol **symbols; guint stack_depth; guint n_symbols; + int pid; g_assert (SYSPROF_IS_CALLGRAPH (self)); g_assert (SYSPROF_IS_DOCUMENT_TRACEABLE (traceable)); + pid = sysprof_document_frame_get_pid (SYSPROF_DOCUMENT_FRAME (traceable)); stack_depth = sysprof_document_traceable_get_stack_depth (traceable); - if (stack_depth > MAX_STACK_DEPTH) + if (stack_depth == 0 || stack_depth > MAX_STACK_DEPTH) return; - symbols = g_newa (SysprofSymbol *, stack_depth); + symbols = g_newa (SysprofSymbol *, stack_depth + 2); n_symbols = sysprof_document_symbolize_traceable (self->document, traceable, symbols, stack_depth); - if (n_symbols == 0) - return; + g_assert (n_symbols > 0); + g_assert (n_symbols <= stack_depth); - trace = g_malloc (sizeof *trace + (n_symbols * sizeof (SysprofCallgraphNode))); - trace->model_position = model_position; - trace->n_nodes = n_symbols; + /* We saved 2 extra spaces for these above so that we can + * tack on the "Process" symbol and the "Everything" symbol. + */ + symbols[n_symbols++] = _sysprof_document_process_symbol (self->document, pid); + symbols[n_symbols++] = self->everything; - for (guint i = 0; i < n_symbols; i++) - { - trace->nodes[i].symbol = symbols[i]; - trace->nodes[i].children = NULL; - trace->nodes[i].parent = NULL; - trace->nodes[i].next = NULL; - trace->nodes[i].prev = NULL; - } + node = sysprof_callgraph_add_trace (self, symbols, n_symbols); - sysprof_callgraph_add_trace (self, trace); + if (node && self->augment_func) + self->augment_func (self, + node, + SYSPROF_DOCUMENT_FRAME (traceable), + self->augment_func_data); } static void @@ -181,18 +236,22 @@ sysprof_callgraph_new_worker (GTask *task, { g_autoptr(SysprofDocumentTraceable) traceable = g_list_model_get_item (self->traceables, i); - sysprof_callgraph_add_traceable (self, traceable, i); + sysprof_callgraph_add_traceable (self, traceable); } g_task_return_pointer (task, g_object_ref (self), g_object_unref); } void -_sysprof_callgraph_new_async (SysprofDocument *document, - GListModel *traceables, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) +_sysprof_callgraph_new_async (SysprofDocument *document, + GListModel *traceables, + gsize augment_size, + SysprofAugmentationFunc augment_func, + gpointer augment_func_data, + GDestroyNotify augment_func_data_destroy, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { g_autoptr(SysprofCallgraph) self = NULL; g_autoptr(GTask) task = NULL; @@ -204,6 +263,10 @@ _sysprof_callgraph_new_async (SysprofDocument *document, self = g_object_new (SYSPROF_TYPE_CALLGRAPH, NULL); self->document = g_object_ref (document); self->traceables = g_object_ref (traceables); + self->augment_size = augment_size; + self->augment_func = augment_func; + self->augment_func_data = augment_func_data; + self->augment_func_data_destroy = augment_func_data_destroy; task = g_task_new (NULL, cancellable, callback, user_data); g_task_set_source_tag (task, _sysprof_callgraph_new_async); @@ -219,3 +282,24 @@ _sysprof_callgraph_new_finish (GAsyncResult *result, return g_task_propagate_pointer (G_TASK (result), error); } + +gpointer +sysprof_callgraph_get_augment (SysprofCallgraph *callgraph, + SysprofCallgraphNode *node) +{ + if (callgraph->augment_size == 0) + return NULL; + else if (callgraph->augment_size <= GLIB_SIZEOF_VOID_P) + return &node->augment; + + if (node->augment == NULL) + node->augment = g_malloc0 (callgraph->augment_size); + + return node->augment; +} + +SysprofCallgraphNode * +sysprof_callgraph_node_parent (SysprofCallgraphNode *node) +{ + return node->parent; +} diff --git a/src/libsysprof-analyze/sysprof-callgraph.h b/src/libsysprof-analyze/sysprof-callgraph.h index 2391758a..5407b62d 100644 --- a/src/libsysprof-analyze/sysprof-callgraph.h +++ b/src/libsysprof-analyze/sysprof-callgraph.h @@ -24,6 +24,8 @@ #include +#include "sysprof-document-frame.h" + G_BEGIN_DECLS #define SYSPROF_TYPE_CALLGRAPH (sysprof_callgraph_get_type()) @@ -31,4 +33,32 @@ G_BEGIN_DECLS SYSPROF_AVAILABLE_IN_ALL G_DECLARE_FINAL_TYPE (SysprofCallgraph, sysprof_callgraph, SYSPROF, CALLGRAPH, GObject) +typedef struct _SysprofCallgraphNode SysprofCallgraphNode; + +/** + * SysprofAugmentationFunc: + * @callgraph: the callgraph being augmented + * @node: the node within the callgraph + * @frame: the frame used to generate this node + * @user_data: closure data for augmentation func + * + * This function is called for the bottom most node in a trace as it is added + * to a callgraph. + * + * The augmentation func should augment the node in whatever way it sees fit + * and generally will want to walk up the node tree to the root to augment the + * parents as it goes. Your augmentation function is not called for each node, + * only the last node. + */ +typedef void (*SysprofAugmentationFunc) (SysprofCallgraph *callgraph, + SysprofCallgraphNode *node, + SysprofDocumentFrame *frame, + gpointer user_data); + +SYSPROF_AVAILABLE_IN_ALL +gpointer sysprof_callgraph_get_augment (SysprofCallgraph *callgraph, + SysprofCallgraphNode *node); +SYSPROF_AVAILABLE_IN_ALL +SysprofCallgraphNode *sysprof_callgraph_node_parent (SysprofCallgraphNode *node); + G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index b30354e0..5a327345 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -1146,19 +1146,35 @@ sysprof_document_callgraph_cb (GObject *object, * sysprof_document_callgraph_async: * @self: a #SysprofDocument * @traceables: a list model of traceables for the callgraph + * @augment_size: the size of data to reserve for augmentation in + * the callgraph. + * @augment_func: (nullable) (scope notified): an optional callback + * to be executed for each node in the callgraph traces to augment + * as the callgraph is generated. + * @augment_func_data: (closure augment_func) (nullable): the closure + * data for @augment_func + * @augment_func_data_destroy: (destroy augment_func) (nullable): the + * destroy callback for @augment_func_data. * @cancellable: (nullable): a #GCancellable or %NULL * @callback: a callback to execute upon completion * @user_data: closure data for @callback * * Asynchronously generates a callgraph using the @traceables to get * the call stacks. + * + * Ideally you want @augment_size to be <= the size of a pointer to + * avoid extra allocations per callgraph node. */ void -sysprof_document_callgraph_async (SysprofDocument *self, - GListModel *traceables, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) +sysprof_document_callgraph_async (SysprofDocument *self, + GListModel *traceables, + gsize augment_size, + SysprofAugmentationFunc augment_func, + gpointer augment_func_data, + GDestroyNotify augment_func_data_destroy, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { g_autoptr(GTask) task = NULL; @@ -1171,6 +1187,10 @@ sysprof_document_callgraph_async (SysprofDocument *self, _sysprof_callgraph_new_async (self, traceables, + augment_size, + augment_func, + augment_func_data, + augment_func_data_destroy, cancellable, sysprof_document_callgraph_cb, g_steal_pointer (&task)); diff --git a/src/libsysprof-analyze/sysprof-document.h b/src/libsysprof-analyze/sysprof-document.h index 6413065e..96377270 100644 --- a/src/libsysprof-analyze/sysprof-document.h +++ b/src/libsysprof-analyze/sysprof-document.h @@ -57,11 +57,15 @@ guint sysprof_document_symbolize_traceable (SysprofDocument SysprofSymbol **symbols, guint n_symbols); SYSPROF_AVAILABLE_IN_ALL -void sysprof_document_callgraph_async (SysprofDocument *self, - GListModel *traceables, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); +void sysprof_document_callgraph_async (SysprofDocument *self, + GListModel *traceables, + gsize augment_size, + SysprofAugmentationFunc augment_func, + gpointer augment_func_data, + GDestroyNotify augment_func_data_destroy, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); SYSPROF_AVAILABLE_IN_ALL SysprofCallgraph *sysprof_document_callgraph_finish (SysprofDocument *self, GAsyncResult *result, diff --git a/src/libsysprof-analyze/tests/meson.build b/src/libsysprof-analyze/tests/meson.build index 9c39724a..152ba494 100644 --- a/src/libsysprof-analyze/tests/meson.build +++ b/src/libsysprof-analyze/tests/meson.build @@ -12,6 +12,7 @@ libsysprof_analyze_testsuite_c_args = [ ] libsysprof_analyze_testsuite = { + 'test-callgraph' : {'skip': true}, 'test-capture-model' : {'skip': true}, 'test-elf-loader' : {'skip': true}, 'test-list-counters' : {'skip': true}, diff --git a/src/libsysprof-analyze/tests/test-callgraph.c b/src/libsysprof-analyze/tests/test-callgraph.c new file mode 100644 index 00000000..94f4ec79 --- /dev/null +++ b/src/libsysprof-analyze/tests/test-callgraph.c @@ -0,0 +1,154 @@ +/* test-callgraph.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include +#include + +#include + +typedef struct _Augment +{ + guint32 size; + guint32 total; +} Augment; + +static const GOptionEntry entries[] = { + { 0 } +}; + +static void +print_callgraph (GListModel *model, + guint depth, + guint total) +{ + guint n_items = g_list_model_get_n_items (model); + + for (guint i = 0; i < n_items; i++) + { + g_autoptr(SysprofCallgraphFrame) frame = g_list_model_get_item (model, i); + SysprofSymbol *symbol = sysprof_callgraph_frame_get_symbol (frame); + Augment *aug = sysprof_callgraph_frame_get_augment (frame); + char tstr[16]; + + g_snprintf (tstr, sizeof tstr, "%.2lf%%", 100. * (aug->total / (double)total)); + + g_print (" [%6u] [%8s] ", aug->total, tstr); + for (guint j = 0; j < depth; j++) + g_print (" "); + g_print ("%s\n", sysprof_symbol_get_name (symbol)); + + print_callgraph (G_LIST_MODEL (frame), depth+1, total); + } +} + +static void +callgraph_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + SysprofDocument *document = SYSPROF_DOCUMENT (object); + GMainLoop *main_loop = user_data; + g_autoptr(GError) error = NULL; + g_autoptr(SysprofCallgraph) callgraph = sysprof_document_callgraph_finish (document, result, &error); + g_autoptr(SysprofCallgraphFrame) root = NULL; + Augment *aug; + + g_assert_no_error (error); + g_assert_true (SYSPROF_IS_CALLGRAPH (callgraph)); + + root = g_list_model_get_item (G_LIST_MODEL (callgraph), 0); + aug = sysprof_callgraph_frame_get_augment (root); + + g_print (" Hits Percent\n"); + print_callgraph (G_LIST_MODEL (callgraph), 0, aug->total); + + g_main_loop_quit (main_loop); +} + +static void +augment_cb (SysprofCallgraph *callgraph, + SysprofCallgraphNode *node, + SysprofDocumentFrame *frame, + gpointer user_data) +{ + Augment *aug; + + g_assert (SYSPROF_IS_CALLGRAPH (callgraph)); + g_assert (node != NULL); + g_assert (SYSPROF_IS_DOCUMENT_SAMPLE (frame)); + g_assert (user_data == NULL); + + for (; node ; node = sysprof_callgraph_node_parent (node)) + { + aug = sysprof_callgraph_get_augment (callgraph, node); + aug->total += 1; + } +} + +int +main (int argc, + char *argv[]) +{ + g_autoptr(GOptionContext) context = g_option_context_new ("- test callgraph generation"); + g_autoptr(GMainLoop) main_loop = g_main_loop_new (NULL, FALSE); + g_autoptr(SysprofDocumentLoader) loader = NULL; + g_autoptr(SysprofDocument) document = NULL; + g_autoptr(SysprofMultiSymbolizer) multi = NULL; + g_autoptr(GListModel) samples = NULL; + g_autoptr(GError) error = NULL; + + setlocale (LC_ALL, ""); + bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); + + g_option_context_add_main_entries (context, entries, NULL); + if (!g_option_context_parse (context, &argc, &argv, &error)) + g_error ("%s", error->message); + + if (argc < 2) + g_error ("usage: %s CAPTURE_FILE", argv[0]); + + multi = sysprof_multi_symbolizer_new (); + 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 ()); + + loader = sysprof_document_loader_new (argv[1]); + sysprof_document_loader_set_symbolizer (loader, SYSPROF_SYMBOLIZER (multi)); + if (!(document = sysprof_document_loader_load (loader, NULL, &error))) + g_error ("Failed to load document: %s", error->message); + + samples = sysprof_document_list_samples (document); + + sysprof_document_callgraph_async (document, + samples, + sizeof (Augment), + augment_cb, NULL, NULL, + NULL, + callgraph_cb, + main_loop); + + g_main_loop_run (main_loop); + + return 0; +} From 7f192958aefb6bf4d257a1760fcf349af97e2347 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 24 May 2023 17:29:32 -0700 Subject: [PATCH 0244/1030] libsysprof-analyze: free callgraph node tree on finalize --- src/libsysprof-analyze/sysprof-callgraph.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-callgraph.c b/src/libsysprof-analyze/sysprof-callgraph.c index a3bf441f..9280fab9 100644 --- a/src/libsysprof-analyze/sysprof-callgraph.c +++ b/src/libsysprof-analyze/sysprof-callgraph.c @@ -80,6 +80,23 @@ list_model_iface_init (GListModelInterface *iface) G_DEFINE_FINAL_TYPE_WITH_CODE (SysprofCallgraph, sysprof_callgraph, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init)) +static void +sysprof_callgraph_node_free (SysprofCallgraphNode *node, + gboolean free_self) +{ + SysprofCallgraphNode *iter = node->children; + + while (iter) + { + SysprofCallgraphNode *to_free = iter; + iter = iter->next; + sysprof_callgraph_node_free (to_free, TRUE); + } + + if (free_self) + g_free (node); +} + static void sysprof_callgraph_dispose (GObject *object) { @@ -107,6 +124,8 @@ sysprof_callgraph_finalize (GObject *object) g_clear_object (&self->traceables); g_clear_object (&self->everything); + sysprof_callgraph_node_free (&self->root, FALSE); + G_OBJECT_CLASS (sysprof_callgraph_parent_class)->finalize (object); } From 007b42a80f95947998dab9931d38165a9f40ebe2 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 24 May 2023 23:31:28 -0700 Subject: [PATCH 0245/1030] libsysprof-analyze: allow setting kallsyms file --- src/libsysprof-analyze/tests/test-callgraph.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/libsysprof-analyze/tests/test-callgraph.c b/src/libsysprof-analyze/tests/test-callgraph.c index 94f4ec79..ece89b8d 100644 --- a/src/libsysprof-analyze/tests/test-callgraph.c +++ b/src/libsysprof-analyze/tests/test-callgraph.c @@ -31,7 +31,9 @@ typedef struct _Augment guint32 total; } Augment; +static char *kallsyms_path; static const GOptionEntry entries[] = { + { "kallsyms", 'k', 0, G_OPTION_ARG_FILENAME, &kallsyms_path, "The path to kallsyms to use for decoding", "PATH" }, { 0 } }; @@ -129,7 +131,19 @@ main (int argc, g_error ("usage: %s CAPTURE_FILE", argv[0]); multi = sysprof_multi_symbolizer_new (); - sysprof_multi_symbolizer_take (multi, sysprof_kallsyms_symbolizer_new ()); + + if (kallsyms_path) + { + g_autoptr(GFile) kallsyms_file = g_file_new_for_path (kallsyms_path); + GFileInputStream *kallsyms_stream = g_file_read (kallsyms_file, NULL, NULL); + + sysprof_multi_symbolizer_take (multi, sysprof_kallsyms_symbolizer_new_for_symbols (G_INPUT_STREAM (kallsyms_stream))); + } + else + { + 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 ()); From 9c8b3b9c25a7c870206fec0c139064d5474cdccb Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 25 May 2023 11:11:52 -0700 Subject: [PATCH 0246/1030] libsysprof-analyze: add bit for context switch symbols --- src/libsysprof-analyze/sysprof-document-symbols.c | 2 ++ src/libsysprof-analyze/sysprof-symbol-private.h | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-document-symbols.c b/src/libsysprof-analyze/sysprof-document-symbols.c index ff63de29..d2d8dda6 100644 --- a/src/libsysprof-analyze/sysprof-document-symbols.c +++ b/src/libsysprof-analyze/sysprof-document-symbols.c @@ -178,6 +178,8 @@ sysprof_document_symbols_worker (GTask *task, { g_autoptr(SysprofSymbol) symbol = _sysprof_symbol_new (g_ref_string_new_intern (context_switches[cs].name), NULL, NULL, 0, 0); + symbol->is_context_switch = TRUE; + /* TODO: It would be nice if we had enough insight from the capture header * as to the host system, so we can show "vmlinuz" and "Linux" respectively * for binary-path and binary-nick when the capture came from Linux. diff --git a/src/libsysprof-analyze/sysprof-symbol-private.h b/src/libsysprof-analyze/sysprof-symbol-private.h index b859155e..4eab3e37 100644 --- a/src/libsysprof-analyze/sysprof-symbol-private.h +++ b/src/libsysprof-analyze/sysprof-symbol-private.h @@ -37,6 +37,8 @@ struct _SysprofSymbol SysprofAddress begin_address; SysprofAddress end_address; + + guint is_context_switch : 1; }; SysprofSymbol *_sysprof_symbol_new (GRefString *name, @@ -58,4 +60,10 @@ _sysprof_symbol_equal (const SysprofSymbol *a, return strcmp (a->name, b->name) == 0; } +static inline gboolean +_sysprof_symbol_is_context_switch (SysprofSymbol *symbol) +{ + return symbol->is_context_switch; +} + G_END_DECLS From 81672f191bf91e92231c6edee5272d185f8ead82 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 25 May 2023 11:12:23 -0700 Subject: [PATCH 0247/1030] libsysprof-analyze: fix symbol name for context switch We want the last context as we're working backwards in the stack trace. --- src/libsysprof-analyze/sysprof-document-symbols.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libsysprof-analyze/sysprof-document-symbols.c b/src/libsysprof-analyze/sysprof-document-symbols.c index d2d8dda6..221c2d04 100644 --- a/src/libsysprof-analyze/sysprof-document-symbols.c +++ b/src/libsysprof-analyze/sysprof-document-symbols.c @@ -273,8 +273,11 @@ _sysprof_document_symbols_lookup (SysprofDocumentSymbols *self, g_return_val_if_fail (SYSPROF_IS_DOCUMENT_SYMBOLS (self), NULL); g_return_val_if_fail (context <= SYSPROF_ADDRESS_CONTEXT_GUEST_USER, NULL); + if (context == SYSPROF_ADDRESS_CONTEXT_NONE) + context = SYSPROF_ADDRESS_CONTEXT_USER; + if (sysprof_address_is_context_switch (address, &new_context)) - return self->context_switches[new_context]; + return self->context_switches[context]; if (context == SYSPROF_ADDRESS_CONTEXT_KERNEL) return sysprof_symbol_cache_lookup (self->kernel_symbols, address); From f79a3c6d2e9c318721685be2f2d310786d26e2e8 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 25 May 2023 11:13:10 -0700 Subject: [PATCH 0248/1030] libsysprof-context: skip context switches at head of trace We can get stack traces which have a USER at the end, or KERNEL at the end, and nothing after it. Not really useful to us. --- src/libsysprof-analyze/sysprof-callgraph.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-callgraph.c b/src/libsysprof-analyze/sysprof-callgraph.c index 9280fab9..9d711d01 100644 --- a/src/libsysprof-analyze/sysprof-callgraph.c +++ b/src/libsysprof-analyze/sysprof-callgraph.c @@ -158,6 +158,16 @@ sysprof_callgraph_add_trace (SysprofCallgraph *self, parent = &self->root; + /* If the first thing we see is a context switch, then there is + * nothing after it to account for. Just skip the symbol as it + * provides nothing to us in the callgraph. + */ + if (_sysprof_symbol_is_context_switch (symbols[0])) + { + symbols++; + n_symbols--; + } + for (guint i = n_symbols - 1; i > 0; i--) { SysprofSymbol *symbol = symbols[i-1]; From f6ec1198992e153fcd6b03bb4ab74375dadc6b7d Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 25 May 2023 11:13:32 -0700 Subject: [PATCH 0249/1030] libsysprof-analyze: move definition to private header So we can access it from other sources. --- .../sysprof-document-symbols-private.h | 10 +++++++++- src/libsysprof-analyze/sysprof-document-symbols.c | 8 -------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-document-symbols-private.h b/src/libsysprof-analyze/sysprof-document-symbols-private.h index e1b61625..7ab2b789 100644 --- a/src/libsysprof-analyze/sysprof-document-symbols-private.h +++ b/src/libsysprof-analyze/sysprof-document-symbols-private.h @@ -22,8 +22,9 @@ #include "sysprof-document.h" #include "sysprof-process-info-private.h" -#include "sysprof-symbolizer.h" +#include "sysprof-symbol-cache-private.h" #include "sysprof-symbol.h" +#include "sysprof-symbolizer.h" G_BEGIN_DECLS @@ -31,6 +32,13 @@ G_BEGIN_DECLS G_DECLARE_FINAL_TYPE (SysprofDocumentSymbols, sysprof_document_symbols, SYSPROF, DOCUMENT_SYMBOLS, GObject) +struct _SysprofDocumentSymbols +{ + GObject parent_instance; + SysprofSymbol *context_switches[SYSPROF_ADDRESS_CONTEXT_GUEST_USER+1]; + SysprofSymbolCache *kernel_symbols; +}; + void _sysprof_document_symbols_new (SysprofDocument *document, SysprofStrings *strings, SysprofSymbolizer *symbolizer, diff --git a/src/libsysprof-analyze/sysprof-document-symbols.c b/src/libsysprof-analyze/sysprof-document-symbols.c index 221c2d04..688c6c7c 100644 --- a/src/libsysprof-analyze/sysprof-document-symbols.c +++ b/src/libsysprof-analyze/sysprof-document-symbols.c @@ -27,16 +27,8 @@ #include "sysprof-mount-namespace-private.h" #include "sysprof-no-symbolizer.h" #include "sysprof-symbol-private.h" -#include "sysprof-symbol-cache-private.h" #include "sysprof-symbolizer-private.h" -struct _SysprofDocumentSymbols -{ - GObject parent_instance; - SysprofSymbol *context_switches[SYSPROF_ADDRESS_CONTEXT_GUEST_USER+1]; - SysprofSymbolCache *kernel_symbols; -}; - G_DEFINE_FINAL_TYPE (SysprofDocumentSymbols, sysprof_document_symbols, G_TYPE_OBJECT) static void From 1b0434c854a2b872cef1132a0dffab68aa9a1cc6 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 25 May 2023 11:13:57 -0700 Subject: [PATCH 0250/1030] libsysprof-analyze: add private API to get the kernel symbol We may need to inject "- - Kernel - -" in some stack traces. --- src/libsysprof-analyze/sysprof-document-private.h | 1 + src/libsysprof-analyze/sysprof-document.c | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-document-private.h b/src/libsysprof-analyze/sysprof-document-private.h index 3a84384e..4e2dddd4 100644 --- a/src/libsysprof-analyze/sysprof-document-private.h +++ b/src/libsysprof-analyze/sysprof-document-private.h @@ -58,5 +58,6 @@ GRefString *_sysprof_document_ref_string (SysprofDocument *self, GtkBitset *_sysprof_document_traceables (SysprofDocument *self); SysprofSymbol *_sysprof_document_process_symbol (SysprofDocument *self, int pid); +SysprofSymbol *_sysprof_document_kernel_symbol (SysprofDocument *self); G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index 5a327345..301ea9ad 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -1233,3 +1233,11 @@ _sysprof_document_process_symbol (SysprofDocument *self, return info->fallback_symbol; } + +SysprofSymbol * +_sysprof_document_kernel_symbol (SysprofDocument *self) +{ + g_return_val_if_fail (SYSPROF_IS_DOCUMENT (self), NULL); + + return self->symbols->context_switches[SYSPROF_ADDRESS_CONTEXT_KERNEL]; +} From 6f90a552e7cea68564b1babd94e8f2a6a0cab283 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 25 May 2023 11:15:07 -0700 Subject: [PATCH 0251/1030] libsysprof-analyze: include final address context We may need to know the final address context so we can inject symbols as necessary into the top of the callgraph. We know it when generating the symbols, so just yield it to the caller too. --- src/libsysprof-analyze/sysprof-callgraph.c | 7 ++++++- src/libsysprof-analyze/sysprof-document.c | 11 +++++++++-- src/libsysprof-analyze/sysprof-document.h | 3 ++- src/libsysprof-analyze/tests/test-symbolize.c | 3 ++- 4 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-callgraph.c b/src/libsysprof-analyze/sysprof-callgraph.c index 9d711d01..b6bff950 100644 --- a/src/libsysprof-analyze/sysprof-callgraph.c +++ b/src/libsysprof-analyze/sysprof-callgraph.c @@ -209,6 +209,7 @@ static void sysprof_callgraph_add_traceable (SysprofCallgraph *self, SysprofDocumentTraceable *traceable) { + SysprofAddressContext final_context; SysprofCallgraphNode *node; SysprofSymbol **symbols; guint stack_depth; @@ -225,7 +226,11 @@ sysprof_callgraph_add_traceable (SysprofCallgraph *self, return; symbols = g_newa (SysprofSymbol *, stack_depth + 2); - n_symbols = sysprof_document_symbolize_traceable (self->document, traceable, symbols, stack_depth); + n_symbols = sysprof_document_symbolize_traceable (self->document, + traceable, + symbols, + stack_depth, + &final_context); g_assert (n_symbols > 0); g_assert (n_symbols <= stack_depth); diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index 301ea9ad..553e2203 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -1062,6 +1062,8 @@ sysprof_document_list_jitmaps (SysprofDocument *self) * @traceable: the traceable to extract symbols for * @symbols: an array to store #SysprofSymbols * @n_symbols: the number of elements in @symbols + * @final_context: (out) (nullable): a location to store the last address + * context of the stack trace. * * Batch symbolizing of a traceable. * @@ -1074,7 +1076,8 @@ guint sysprof_document_symbolize_traceable (SysprofDocument *self, SysprofDocumentTraceable *traceable, SysprofSymbol **symbols, - guint n_symbols) + guint n_symbols, + SysprofAddressContext *final_context) { SysprofAddressContext last_context = SYSPROF_ADDRESS_CONTEXT_NONE; const SysprofProcessInfo *process_info; @@ -1087,7 +1090,7 @@ sysprof_document_symbolize_traceable (SysprofDocument *self, g_return_val_if_fail (SYSPROF_IS_DOCUMENT_TRACEABLE (traceable), 0); if (n_symbols == 0 || symbols == NULL) - return 0; + goto finish; pid = sysprof_document_frame_get_pid (SYSPROF_DOCUMENT_FRAME (traceable)); process_info = g_hash_table_lookup (self->pid_to_process_info, GINT_TO_POINTER (pid)); @@ -1107,6 +1110,10 @@ sysprof_document_symbolize_traceable (SysprofDocument *self, last_context = context; } +finish: + if (final_context) + *final_context = last_context; + return n_symbolized; } diff --git a/src/libsysprof-analyze/sysprof-document.h b/src/libsysprof-analyze/sysprof-document.h index 96377270..4cb29292 100644 --- a/src/libsysprof-analyze/sysprof-document.h +++ b/src/libsysprof-analyze/sysprof-document.h @@ -55,7 +55,8 @@ SYSPROF_AVAILABLE_IN_ALL guint sysprof_document_symbolize_traceable (SysprofDocument *self, SysprofDocumentTraceable *traceable, SysprofSymbol **symbols, - guint n_symbols); + guint n_symbols, + SysprofAddressContext *final_context); SYSPROF_AVAILABLE_IN_ALL void sysprof_document_callgraph_async (SysprofDocument *self, GListModel *traceables, diff --git a/src/libsysprof-analyze/tests/test-symbolize.c b/src/libsysprof-analyze/tests/test-symbolize.c index ec788011..31b6b1ab 100644 --- a/src/libsysprof-analyze/tests/test-symbolize.c +++ b/src/libsysprof-analyze/tests/test-symbolize.c @@ -62,7 +62,8 @@ load_cb (GObject *object, n_symbols = sysprof_document_symbolize_traceable (document, traceable, symbols, - G_N_ELEMENTS (symbols)); + G_N_ELEMENTS (symbols), + NULL); g_print ("%s depth=%u pid=%u\n", G_OBJECT_TYPE_NAME (traceable), From 8353b1eb9c1f4c1fd63ab543cbe0a397e7cf32fe Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 25 May 2023 11:16:08 -0700 Subject: [PATCH 0252/1030] libsysprof-analyze: account kernel stacks to the kernel If our entire stack was in kernel address context, inject the "- - Kernel - -" symbol at the top of the stack trace so that accounting gets properly assigned to the kernel. This is typical with kernel processes such as kworker. --- src/libsysprof-analyze/sysprof-callgraph.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-callgraph.c b/src/libsysprof-analyze/sysprof-callgraph.c index b6bff950..f77c7e9f 100644 --- a/src/libsysprof-analyze/sysprof-callgraph.c +++ b/src/libsysprof-analyze/sysprof-callgraph.c @@ -225,7 +225,7 @@ sysprof_callgraph_add_traceable (SysprofCallgraph *self, if (stack_depth == 0 || stack_depth > MAX_STACK_DEPTH) return; - symbols = g_newa (SysprofSymbol *, stack_depth + 2); + symbols = g_newa (SysprofSymbol *, stack_depth + 3); n_symbols = sysprof_document_symbolize_traceable (self->document, traceable, symbols, @@ -235,9 +235,14 @@ sysprof_callgraph_add_traceable (SysprofCallgraph *self, g_assert (n_symbols > 0); g_assert (n_symbols <= stack_depth); - /* We saved 2 extra spaces for these above so that we can + /* We saved 3 extra spaces for these above so that we can * tack on the "Process" symbol and the "Everything" symbol. + * If the final address context places us in Kernel, we want + * to add a "- - Kernel - -" symbol to ensure that we are + * accounting cost to the kernel for the process. */ + if (final_context == SYSPROF_ADDRESS_CONTEXT_KERNEL) + symbols[n_symbols++] = _sysprof_document_kernel_symbol (self->document); symbols[n_symbols++] = _sysprof_document_process_symbol (self->document, pid); symbols[n_symbols++] = self->everything; From b5ce671e23fed0b9ec57a34b25aa046bc277b88b Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 25 May 2023 12:16:18 -0700 Subject: [PATCH 0253/1030] tools: start on callgraph example using columnview This just serves as a prototyping ground so that we can have a callgraph view widget in the future based on these principles. It also shows some areas that still need work, such as sorting within the tree and fixing the text offset calculation for ELF symbols. --- src/tools/callgraph.c | 357 ++++++++++++++++++++++++++++++++++++++++++ src/tools/meson.build | 9 ++ 2 files changed, 366 insertions(+) create mode 100644 src/tools/callgraph.c diff --git a/src/tools/callgraph.c b/src/tools/callgraph.c new file mode 100644 index 00000000..76a793eb --- /dev/null +++ b/src/tools/callgraph.c @@ -0,0 +1,357 @@ +/* callgraph.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include +#include + +static GMainLoop *main_loop; +static char *kallsyms_path; +static char *filename; +static double total; +static const GOptionEntry entries[] = { + { "kallsyms", 'k', 0, G_OPTION_ARG_FILENAME, &kallsyms_path, "The path to kallsyms to use for decoding", "PATH" }, + { 0 } +}; + +typedef struct _Augment +{ + guint32 size; + guint32 total; +} Augment; + +static void +function_setup (GtkSignalListItemFactory *factory, + GtkListItem *list_item, + SysprofCallgraph *callgraph) +{ + GtkWidget *expander; + GtkWidget *text; + + g_assert (GTK_IS_SIGNAL_LIST_ITEM_FACTORY (factory)); + g_assert (GTK_IS_LIST_ITEM (list_item)); + g_assert (SYSPROF_IS_CALLGRAPH (callgraph)); + + expander = gtk_tree_expander_new (); + text = g_object_new (GTK_TYPE_INSCRIPTION, + "hexpand", TRUE, + NULL); + gtk_tree_expander_set_child (GTK_TREE_EXPANDER (expander), text); + gtk_list_item_set_child (list_item, expander); +} + +static void +function_bind (GtkSignalListItemFactory *factory, + GtkListItem *list_item, + SysprofCallgraph *callgraph) +{ + SysprofCallgraphFrame *frame; + GtkTreeListRow *row; + SysprofSymbol *symbol; + const char *name; + GtkWidget *expander; + GtkWidget *text; + + g_assert (GTK_IS_SIGNAL_LIST_ITEM_FACTORY (factory)); + g_assert (GTK_IS_LIST_ITEM (list_item)); + g_assert (SYSPROF_IS_CALLGRAPH (callgraph)); + + row = gtk_list_item_get_item (list_item); + frame = gtk_tree_list_row_get_item (row); + symbol = sysprof_callgraph_frame_get_symbol (frame); + name = sysprof_symbol_get_name (symbol); + + expander = gtk_list_item_get_child (list_item); + text = gtk_tree_expander_get_child (GTK_TREE_EXPANDER (expander)); + gtk_tree_expander_set_list_row (GTK_TREE_EXPANDER (expander), row); + gtk_inscription_set_text (GTK_INSCRIPTION (text), name); +} + +static void +total_setup (GtkSignalListItemFactory *factory, + GtkListItem *list_item, + SysprofCallgraph *callgraph) +{ + GtkWidget *child; + + g_assert (GTK_IS_SIGNAL_LIST_ITEM_FACTORY (factory)); + g_assert (GTK_IS_LIST_ITEM (list_item)); + g_assert (SYSPROF_IS_CALLGRAPH (callgraph)); + + child = g_object_new (GTK_TYPE_PROGRESS_BAR, + "show-text", TRUE, + NULL); + gtk_list_item_set_child (list_item, child); +} + +static void +total_bind (GtkSignalListItemFactory *factory, + GtkListItem *list_item, + gpointer user_data) +{ + g_autofree char *text = NULL; + SysprofCallgraphFrame *frame; + GtkTreeListRow *row; + GtkWidget *child; + double fraction; + Augment *aug; + + g_assert (GTK_IS_SIGNAL_LIST_ITEM_FACTORY (factory)); + g_assert (GTK_IS_LIST_ITEM (list_item)); + + row = gtk_list_item_get_item (list_item); + frame = gtk_tree_list_row_get_item (row); + aug = sysprof_callgraph_frame_get_augment (frame); + fraction = aug->total / total; + + text = g_strdup_printf ("%.2lf", fraction*100.); + + child = gtk_list_item_get_child (list_item); + gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (child), fraction); + gtk_progress_bar_set_text (GTK_PROGRESS_BAR (child), text); +} + +static void +self_setup (GtkSignalListItemFactory *factory, + GtkListItem *list_item, + SysprofCallgraph *callgraph) +{ + GtkWidget *child; + + g_assert (GTK_IS_SIGNAL_LIST_ITEM_FACTORY (factory)); + g_assert (GTK_IS_LIST_ITEM (list_item)); + g_assert (SYSPROF_IS_CALLGRAPH (callgraph)); + + child = g_object_new (GTK_TYPE_PROGRESS_BAR, + "show-text", TRUE, + NULL); + gtk_list_item_set_child (list_item, child); +} + +static void +self_bind (GtkSignalListItemFactory *factory, + GtkListItem *list_item, + gpointer user_data) +{ + g_autofree char *text = NULL; + SysprofCallgraphFrame *frame; + GtkTreeListRow *row; + GtkWidget *child; + double fraction; + Augment *aug; + + g_assert (GTK_IS_SIGNAL_LIST_ITEM_FACTORY (factory)); + g_assert (GTK_IS_LIST_ITEM (list_item)); + + row = gtk_list_item_get_item (list_item); + frame = gtk_tree_list_row_get_item (row); + aug = sysprof_callgraph_frame_get_augment (frame); + fraction = aug->size / total; + + text = g_strdup_printf ("%.2lf", fraction*100.); + + child = gtk_list_item_get_child (list_item); + gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (child), fraction); + gtk_progress_bar_set_text (GTK_PROGRESS_BAR (child), text); +} + +static GListModel * +create_model_func (gpointer item, + gpointer user_data) +{ + if (g_list_model_get_n_items (G_LIST_MODEL (item)) > 0) + return g_object_ref (G_LIST_MODEL (item)); + return NULL; +} + +static void +show_callgraph (SysprofCallgraph *callgraph) +{ + g_autofree char *name = g_path_get_basename (filename); + g_autoptr(GtkTreeListModel) tree = NULL; + g_autoptr(GtkNoSelection) model = NULL; + g_autoptr(SysprofCallgraphFrame) root = NULL; + GtkColumnViewColumn *column; + GtkScrolledWindow *scroller; + GtkColumnView *column_view; + GtkListItemFactory *factory; + GtkWindow *window; + Augment *aug; + + root = g_list_model_get_item (G_LIST_MODEL (callgraph), 0); + aug = sysprof_callgraph_frame_get_augment (root); + total = aug->total; + + tree = gtk_tree_list_model_new (g_object_ref (G_LIST_MODEL (callgraph)), + FALSE, + FALSE, + create_model_func, NULL, NULL); + model = gtk_no_selection_new (g_object_ref (G_LIST_MODEL (tree))); + + window = g_object_new (GTK_TYPE_WINDOW, + "default-width", 1024, + "default-height", 800, + "title", name, + NULL); + scroller = g_object_new (GTK_TYPE_SCROLLED_WINDOW, NULL); + gtk_window_set_child (GTK_WINDOW (window), GTK_WIDGET (scroller)); + column_view = g_object_new (GTK_TYPE_COLUMN_VIEW, + "model", model, + NULL); + gtk_scrolled_window_set_child (scroller, GTK_WIDGET (column_view)); + + factory = gtk_signal_list_item_factory_new (); + g_signal_connect_object (factory, "setup", G_CALLBACK (function_setup), callgraph, 0); + g_signal_connect_object (factory, "bind", G_CALLBACK (function_bind), callgraph, 0); + column = gtk_column_view_column_new ("Function", factory); + gtk_column_view_column_set_expand (column, TRUE); + gtk_column_view_append_column (column_view, column); + + factory = gtk_signal_list_item_factory_new (); + g_signal_connect_object (factory, "setup", G_CALLBACK (self_setup), callgraph, 0); + g_signal_connect (factory, "bind", G_CALLBACK (self_bind), NULL); + column = gtk_column_view_column_new ("Self", factory); + gtk_column_view_column_set_expand (column, FALSE); + gtk_column_view_append_column (column_view, column); + + factory = gtk_signal_list_item_factory_new (); + g_signal_connect_object (factory, "setup", G_CALLBACK (total_setup), callgraph, 0); + g_signal_connect (factory, "bind", G_CALLBACK (total_bind), NULL); + column = gtk_column_view_column_new ("Total", factory); + gtk_column_view_column_set_expand (column, FALSE); + gtk_column_view_append_column (column_view, column); + + g_signal_connect_swapped (window, + "close-request", + G_CALLBACK (g_main_loop_quit), + main_loop); + gtk_window_present (window); +} + +static void +callgraph_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + SysprofDocument *document = SYSPROF_DOCUMENT (object); + g_autoptr(GError) error = NULL; + g_autoptr(SysprofCallgraph) callgraph = sysprof_document_callgraph_finish (document, result, &error); + + g_print ("Done.\n"); + + g_assert_no_error (error); + g_assert_true (SYSPROF_IS_CALLGRAPH (callgraph)); + + show_callgraph (callgraph); +} + +static void +augment_cb (SysprofCallgraph *callgraph, + SysprofCallgraphNode *node, + SysprofDocumentFrame *frame, + gpointer user_data) +{ + Augment *aug; + + g_assert (SYSPROF_IS_CALLGRAPH (callgraph)); + g_assert (node != NULL); + g_assert (SYSPROF_IS_DOCUMENT_SAMPLE (frame)); + g_assert (user_data == NULL); + + aug = sysprof_callgraph_get_augment (callgraph, node); + aug->size += 1; + + for (; node; node = sysprof_callgraph_node_parent (node)) + { + aug = sysprof_callgraph_get_augment (callgraph, node); + aug->total += 1; + } +} + +int +main (int argc, + char *argv[]) +{ + g_autoptr(GOptionContext) context = g_option_context_new ("- show a callgraph"); + g_autoptr(SysprofDocumentLoader) loader = NULL; + g_autoptr(SysprofDocument) document = NULL; + g_autoptr(SysprofMultiSymbolizer) multi = NULL; + g_autoptr(GListModel) samples = NULL; + g_autoptr(GError) error = NULL; + + gtk_init (); + sysprof_clock_init (); + + g_option_context_add_main_entries (context, entries, NULL); + if (!g_option_context_parse (context, &argc, &argv, &error)) + { + g_printerr ("%s\n", error->message); + return 1; + } + + if (argc != 2) + { + g_print ("usage: %s [OPTIONS] CAPTURE_FILE\n", argv[0]); + return 1; + } + + filename = argv[1]; + + main_loop = g_main_loop_new (NULL, FALSE); + + multi = sysprof_multi_symbolizer_new (); + + if (kallsyms_path) + { + g_autoptr(GFile) kallsyms_file = g_file_new_for_path (kallsyms_path); + GFileInputStream *kallsyms_stream = g_file_read (kallsyms_file, NULL, NULL); + + sysprof_multi_symbolizer_take (multi, sysprof_kallsyms_symbolizer_new_for_symbols (G_INPUT_STREAM (kallsyms_stream))); + } + else + { + 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 ()); + + loader = sysprof_document_loader_new (filename); + sysprof_document_loader_set_symbolizer (loader, SYSPROF_SYMBOLIZER (multi)); + + g_print ("Loading %s, ignoring embedded symbols...\n", filename); + if (!(document = sysprof_document_loader_load (loader, NULL, &error))) + g_error ("Failed to load document: %s", error->message); + + g_print ("Loaded and symbolized. Generating callgraph...\n"); + samples = sysprof_document_list_samples (document); + sysprof_document_callgraph_async (document, + samples, + sizeof (Augment), + augment_cb, NULL, NULL, + NULL, + callgraph_cb, + main_loop); + + g_main_loop_run (main_loop); + + return 0; +} diff --git a/src/tools/meson.build b/src/tools/meson.build index c48c4e10..295e534c 100644 --- a/src/tools/meson.build +++ b/src/tools/meson.build @@ -56,3 +56,12 @@ if get_option('agent') install: true, ) endif + +if get_option('gtk') + callgraph = executable('callgraph', ['callgraph.c'], + dependencies: [libsysprof_analyze_static_dep, + dependency('gtk4', version: gtk_req_version)], + c_args: tools_cflags, + install: false, + ) +endif From c5f4d64a4c2f5b5ae61730fb8aeb23868c99caf0 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 25 May 2023 12:53:13 -0700 Subject: [PATCH 0254/1030] libsysprof-analyze: use old style relative address generation Copy this from the old decoder so it's not something to worry about. --- src/libsysprof-analyze/sysprof-elf-symbolizer.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libsysprof-analyze/sysprof-elf-symbolizer.c b/src/libsysprof-analyze/sysprof-elf-symbolizer.c index aad691ff..afc779c8 100644 --- a/src/libsysprof-analyze/sysprof-elf-symbolizer.c +++ b/src/libsysprof-analyze/sysprof-elf-symbolizer.c @@ -93,7 +93,10 @@ sysprof_elf_symbolizer_symbolize (SysprofSymbolizer *symbolizer, g_assert (address < map_end); file_offset = sysprof_document_mmap_get_file_offset (map); - relative_address = file_offset + (address - map_begin); + + relative_address = address; + relative_address -= map_begin; + relative_address += file_offset; path = sysprof_document_mmap_get_file (map); build_id = sysprof_document_mmap_get_build_id (map); From ed0fefc721c9545fbe7d2c8c4d1ad624fe277592 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 25 May 2023 12:54:02 -0700 Subject: [PATCH 0255/1030] libsysprof-analyze: apply text_offset for elfparser We need to do what binfile was doing and make the address relative to the text_offset. We also need to ignore the text offset of the debuglink files and pass it the text_offset of the original ELF. This fixes a bunch of symbolization in the callgraph. --- src/libsysprof-analyze/sysprof-elf.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-elf.c b/src/libsysprof-analyze/sysprof-elf.c index 8aae956b..04ad5597 100644 --- a/src/libsysprof-analyze/sysprof-elf.c +++ b/src/libsysprof-analyze/sysprof-elf.c @@ -33,6 +33,7 @@ struct _SysprofElf SysprofElf *debug_link_elf; ElfParser *parser; guint64 file_inode; + gulong text_offset; }; enum { @@ -308,6 +309,7 @@ sysprof_elf_new (const char *filename, self->file = g_strdup (filename); self->parser = g_steal_pointer (&parser); self->file_inode = file_inode; + self->text_offset = elf_parser_get_text_offset (self->parser); if (filename != NULL) { @@ -357,7 +359,8 @@ sysprof_elf_get_symbol_at_address_internal (SysprofElf *self, const char *filename, guint64 address, guint64 *begin_address, - guint64 *end_address) + guint64 *end_address, + guint64 text_offset) { const ElfSym *symbol; char *ret = NULL; @@ -368,18 +371,22 @@ 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); + 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; } - if ((symbol = elf_parser_lookup_symbol (self->parser, address))) + if ((symbol = elf_parser_lookup_symbol (self->parser, address - text_offset))) { const char *name; if (begin_address || end_address) - elf_parser_get_sym_address_range (self->parser, symbol, &begin, &end); + { + elf_parser_get_sym_address_range (self->parser, symbol, &begin, &end); + begin += text_offset; + end += text_offset; + } name = elf_parser_get_sym_name (self->parser, symbol); @@ -414,7 +421,8 @@ sysprof_elf_get_symbol_at_address (SysprofElf *self, self->file, address, begin_address, - end_address); + end_address, + self->text_offset); } /** From a9e14be37e58dfc892ca533f89c0206a2ea6f8fb Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 25 May 2023 13:18:26 -0700 Subject: [PATCH 0256/1030] tools: column sorting by default --- src/tools/callgraph.c | 69 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 60 insertions(+), 9 deletions(-) diff --git a/src/tools/callgraph.c b/src/tools/callgraph.c index 76a793eb..2bc0dfaf 100644 --- a/src/tools/callgraph.c +++ b/src/tools/callgraph.c @@ -182,17 +182,62 @@ create_model_func (gpointer item, return NULL; } +static int +sort_by_self (gconstpointer a, + gconstpointer b, + gpointer user_data) +{ + SysprofCallgraphFrame *frame_a = (SysprofCallgraphFrame *)a; + SysprofCallgraphFrame *frame_b = (SysprofCallgraphFrame *)b; + Augment *aug_a = sysprof_callgraph_frame_get_augment (frame_a); + Augment *aug_b = sysprof_callgraph_frame_get_augment (frame_b); + double self_a = aug_a->size / total; + double self_b = aug_b->size / total; + + if (self_a < self_b) + return -1; + else if (self_a > self_b) + return 1; + else + return 0; +} + +static int +sort_by_total (gconstpointer a, + gconstpointer b, + gpointer user_data) +{ + SysprofCallgraphFrame *frame_a = (SysprofCallgraphFrame *)a; + SysprofCallgraphFrame *frame_b = (SysprofCallgraphFrame *)b; + Augment *aug_a = sysprof_callgraph_frame_get_augment (frame_a); + Augment *aug_b = sysprof_callgraph_frame_get_augment (frame_b); + double total_a = aug_a->total / total; + double total_b = aug_b->total / total; + + if (total_a < total_b) + return -1; + else if (total_a > total_b) + return 1; + else + return 0; +} + static void show_callgraph (SysprofCallgraph *callgraph) { g_autofree char *name = g_path_get_basename (filename); g_autoptr(GtkTreeListModel) tree = NULL; - g_autoptr(GtkNoSelection) model = NULL; + g_autoptr(GtkMultiSelection) model = NULL; g_autoptr(SysprofCallgraphFrame) root = NULL; GtkColumnViewColumn *column; GtkScrolledWindow *scroller; GtkColumnView *column_view; GtkListItemFactory *factory; + g_autoptr(GtkTreeListRowSorter) sorter = NULL; + g_autoptr(GtkSortListModel) sort_model = NULL; + g_autoptr(GtkCustomSorter) self_sorter = NULL; + g_autoptr(GtkCustomSorter) total_sorter = NULL; + GtkSorter *column_sorter = NULL; GtkWindow *window; Augment *aug; @@ -200,11 +245,8 @@ show_callgraph (SysprofCallgraph *callgraph) aug = sysprof_callgraph_frame_get_augment (root); total = aug->total; - tree = gtk_tree_list_model_new (g_object_ref (G_LIST_MODEL (callgraph)), - FALSE, - FALSE, - create_model_func, NULL, NULL); - model = gtk_no_selection_new (g_object_ref (G_LIST_MODEL (tree))); + self_sorter = gtk_custom_sorter_new (sort_by_self, NULL, NULL); + total_sorter = gtk_custom_sorter_new (sort_by_total, NULL, NULL); window = g_object_new (GTK_TYPE_WINDOW, "default-width", 1024, @@ -213,11 +255,17 @@ show_callgraph (SysprofCallgraph *callgraph) NULL); scroller = g_object_new (GTK_TYPE_SCROLLED_WINDOW, NULL); gtk_window_set_child (GTK_WINDOW (window), GTK_WIDGET (scroller)); - column_view = g_object_new (GTK_TYPE_COLUMN_VIEW, - "model", model, - NULL); + column_view = g_object_new (GTK_TYPE_COLUMN_VIEW, NULL); gtk_scrolled_window_set_child (scroller, GTK_WIDGET (column_view)); + tree = gtk_tree_list_model_new (g_object_ref (G_LIST_MODEL (callgraph)), + FALSE, FALSE, create_model_func, NULL, NULL); + column_sorter = gtk_column_view_get_sorter (column_view); + sorter = gtk_tree_list_row_sorter_new (g_object_ref (column_sorter)); + sort_model = gtk_sort_list_model_new (g_object_ref (G_LIST_MODEL (tree)), g_object_ref (GTK_SORTER (sorter))); + model = gtk_multi_selection_new (g_object_ref (G_LIST_MODEL (sort_model))); + gtk_column_view_set_model (column_view, GTK_SELECTION_MODEL (model)); + factory = gtk_signal_list_item_factory_new (); g_signal_connect_object (factory, "setup", G_CALLBACK (function_setup), callgraph, 0); g_signal_connect_object (factory, "bind", G_CALLBACK (function_bind), callgraph, 0); @@ -230,6 +278,7 @@ show_callgraph (SysprofCallgraph *callgraph) g_signal_connect (factory, "bind", G_CALLBACK (self_bind), NULL); column = gtk_column_view_column_new ("Self", factory); gtk_column_view_column_set_expand (column, FALSE); + gtk_column_view_column_set_sorter (column, GTK_SORTER (self_sorter)); gtk_column_view_append_column (column_view, column); factory = gtk_signal_list_item_factory_new (); @@ -237,7 +286,9 @@ show_callgraph (SysprofCallgraph *callgraph) g_signal_connect (factory, "bind", G_CALLBACK (total_bind), NULL); column = gtk_column_view_column_new ("Total", factory); gtk_column_view_column_set_expand (column, FALSE); + gtk_column_view_column_set_sorter (column, GTK_SORTER (total_sorter)); gtk_column_view_append_column (column_view, column); + gtk_column_view_sort_by_column (column_view, column, GTK_SORT_DESCENDING); g_signal_connect_swapped (window, "close-request", From eaee76e49f941235793b24de9e4684aa0259bdcc Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 25 May 2023 13:36:40 -0700 Subject: [PATCH 0257/1030] tools: use data-table class --- src/tools/callgraph.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tools/callgraph.c b/src/tools/callgraph.c index 2bc0dfaf..91ac2d51 100644 --- a/src/tools/callgraph.c +++ b/src/tools/callgraph.c @@ -256,6 +256,7 @@ show_callgraph (SysprofCallgraph *callgraph) scroller = g_object_new (GTK_TYPE_SCROLLED_WINDOW, NULL); gtk_window_set_child (GTK_WINDOW (window), GTK_WIDGET (scroller)); column_view = g_object_new (GTK_TYPE_COLUMN_VIEW, NULL); + gtk_widget_add_css_class (GTK_WIDGET (column_view), "data-table"); gtk_scrolled_window_set_child (scroller, GTK_WIDGET (column_view)); tree = gtk_tree_list_model_new (g_object_ref (G_LIST_MODEL (callgraph)), From 503d3decca07a815f2205e947131cd13791dc09b Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 25 May 2023 14:33:08 -0700 Subject: [PATCH 0258/1030] libsysprof-analyze: track nodes that were toplevel That way when we want to get a list of stack traces, we could know to yield a trace stopping at this node. --- src/libsysprof-analyze/tests/test-callgraph.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/libsysprof-analyze/tests/test-callgraph.c b/src/libsysprof-analyze/tests/test-callgraph.c index ece89b8d..03479b2c 100644 --- a/src/libsysprof-analyze/tests/test-callgraph.c +++ b/src/libsysprof-analyze/tests/test-callgraph.c @@ -27,8 +27,9 @@ typedef struct _Augment { - guint32 size; - guint32 total; + guint64 toplevel : 1; + guint64 size : 31; + guint32 total : 32; } Augment; static char *kallsyms_path; @@ -99,6 +100,9 @@ augment_cb (SysprofCallgraph *callgraph, g_assert (SYSPROF_IS_DOCUMENT_SAMPLE (frame)); g_assert (user_data == NULL); + aug = sysprof_callgraph_get_augment (callgraph, node); + aug->toplevel = TRUE; + for (; node ; node = sysprof_callgraph_node_parent (node)) { aug = sysprof_callgraph_get_augment (callgraph, node); From 7ec74308b5b7fd6a61e7465b422bdecea475d579 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 25 May 2023 15:10:45 -0700 Subject: [PATCH 0259/1030] build: update required compiler versions --- meson.build | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/meson.build b/meson.build index 1db87099..e9e22034 100644 --- a/meson.build +++ b/meson.build @@ -2,8 +2,8 @@ project('sysprof', 'c', license: ['GPL3+', 'GPL2+'], version: '45.alpha', meson_version: '>=0.59.0', - default_options: [ 'c_std=gnu11', - 'cpp_std=c++11', + default_options: [ 'c_std=gnu17', + 'cpp_std=gnu++17', 'warning_level=2', ] ) @@ -161,7 +161,7 @@ test_c_args = [ '-Wswitch-default', '-Wswitch-enum', '-Wuninitialized', - ['-Werror=format-security', '-Werror=format=2' ], + ['-Werror=format-security', '-Werror=format=2'], '-Werror=empty-body', '-Werror=implicit-function-declaration', '-Werror=pointer-arith', From 92b3b77dd20c26089b0f1b64e7f7d3a4c47a6783 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 25 May 2023 14:54:28 -0700 Subject: [PATCH 0260/1030] contrib: create static library for elfparser I want to move a bunch of this "contrib" style sources into their own area so we can statically link them but keep them separate from main sysprof code. --- contrib/elfparser/demangle.cpp | 40 ++ contrib/elfparser/demangle.h | 28 + contrib/elfparser/elfparser.c | 903 +++++++++++++++++++++++++++++ contrib/elfparser/elfparser.h | 68 +++ contrib/elfparser/meson.build | 19 + contrib/meson.build | 1 + meson.build | 1 + src/libsysprof-analyze/meson.build | 4 +- 8 files changed, 1061 insertions(+), 3 deletions(-) create mode 100644 contrib/elfparser/demangle.cpp create mode 100644 contrib/elfparser/demangle.h create mode 100644 contrib/elfparser/elfparser.c create mode 100644 contrib/elfparser/elfparser.h create mode 100644 contrib/elfparser/meson.build create mode 100644 contrib/meson.build diff --git a/contrib/elfparser/demangle.cpp b/contrib/elfparser/demangle.cpp new file mode 100644 index 00000000..1f941cd2 --- /dev/null +++ b/contrib/elfparser/demangle.cpp @@ -0,0 +1,40 @@ +/* demangle.cpp + * + * Copyright (C) 2016 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 . + */ + +#include +#include + +#include "demangle.h" + +gchar * +sysprof_cplus_demangle (const gchar *name) +{ + char *real_name; + gchar *ret; + int status; + + real_name = abi::__cxa_demangle (name, 0, 0, &status); + + if (real_name == NULL) + return NULL; + + ret = g_strdup (real_name); + free (real_name); + + return ret; +} diff --git a/contrib/elfparser/demangle.h b/contrib/elfparser/demangle.h new file mode 100644 index 00000000..26d28fdc --- /dev/null +++ b/contrib/elfparser/demangle.h @@ -0,0 +1,28 @@ +/* demangle.h + * + * Copyright 2016-2019 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 . + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +G_GNUC_INTERNAL +gchar *sysprof_cplus_demangle (const gchar *name); + +G_END_DECLS diff --git a/contrib/elfparser/elfparser.c b/contrib/elfparser/elfparser.c new file mode 100644 index 00000000..2c6b638b --- /dev/null +++ b/contrib/elfparser/elfparser.c @@ -0,0 +1,903 @@ +/* Sysprof -- Sampling, systemwide CPU profiler + * Copyright 2006, 2007, Soeren Sandmann + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#ifdef __APPLE__ +# include +#else +# include +#endif +#include + +#include "demangle.h" +#include "elfparser.h" + +typedef struct Section Section; + +struct ElfSym +{ + gulong table; + gulong offset; + gulong address; +}; + +struct Section +{ + const gchar * name; + gsize offset; + gsize size; + gboolean allocated; + gulong load_address; + guint type; +}; + +struct ElfParser +{ + gboolean is_64; + const guchar * data; + gsize length; + + guint n_sections; + Section ** sections; + + guint n_symbols; + ElfSym * symbols; + gsize sym_strings; + + GMappedFile * file; + + char * filename; + + gboolean checked_build_id; + char * build_id; + + const Section * text_section; +}; + +/* FIXME: All of these should in principle do endian swapping, + * but sysprof never has to deal with binaries of a different + * endianness than sysprof itself + */ +#define GET_FIELD(parser, offset, struct_name, idx, field_name) \ + (((parser))->is_64? \ + ((Elf64_ ## struct_name *)(gpointer)(((parser)->data + offset)) + (idx))->field_name : \ + ((Elf32_ ## struct_name *)(gpointer)(((parser)->data + offset)) + (idx))->field_name) + +#define GET_UINT32(parser, offset) \ + *((uint32_t *)(gpointer)(parser->data + offset)) \ + +#define GET_SIZE(parser, struct_name) \ + (((parser)->is_64? \ + sizeof (Elf64_ ## struct_name) : \ + sizeof (Elf32_ ## struct_name))) + +#define MAKE_ELF_UINT_ACCESSOR(field_name) \ + static uint64_t field_name (ElfParser *parser) \ + { \ + return GET_FIELD (parser, 0, Ehdr, 0, field_name); \ + } + +MAKE_ELF_UINT_ACCESSOR (e_shoff) +MAKE_ELF_UINT_ACCESSOR (e_shnum) +MAKE_ELF_UINT_ACCESSOR (e_shstrndx) + +#define MAKE_SECTION_HEADER_ACCESSOR(field_name) \ + static uint64_t field_name (ElfParser *parser, int nth_section) \ + { \ + gsize offset = e_shoff (parser); \ + \ + return GET_FIELD (parser, offset, Shdr, nth_section, field_name); \ + } + +MAKE_SECTION_HEADER_ACCESSOR (sh_name); +MAKE_SECTION_HEADER_ACCESSOR (sh_type); +MAKE_SECTION_HEADER_ACCESSOR (sh_flags); +MAKE_SECTION_HEADER_ACCESSOR (sh_addr); +MAKE_SECTION_HEADER_ACCESSOR (sh_offset); +MAKE_SECTION_HEADER_ACCESSOR (sh_size); + +#define MAKE_SYMBOL_ACCESSOR(field_name) \ + static uint64_t field_name (ElfParser *parser, gulong offset, gulong nth) \ + { \ + return GET_FIELD (parser, offset, Sym, nth, field_name); \ + } + +MAKE_SYMBOL_ACCESSOR(st_name); +MAKE_SYMBOL_ACCESSOR(st_info); +MAKE_SYMBOL_ACCESSOR(st_value); +MAKE_SYMBOL_ACCESSOR(st_size); +MAKE_SYMBOL_ACCESSOR(st_shndx); + +static gboolean +in_container (void) +{ + static gboolean _in_container; + static gboolean initialized; + + if (!initialized) + { + /* Flatpak has /.flatpak-info + * Podman has /run/.containerenv + * + * Both have access to host files via /var/run/host. + */ + _in_container = g_file_test ("/.flatpak-info", G_FILE_TEST_EXISTS) || + g_file_test ("/run/.containerenv", G_FILE_TEST_EXISTS); + + initialized = TRUE; + } + + return _in_container; +} + +static void +section_free (Section *section) +{ + g_free (section); +} + +static const Section * +find_section (ElfParser *parser, + const char *name, + guint type) +{ + guint i; + + for (i = 0; i < parser->n_sections; ++i) + { + Section *section = parser->sections[i]; + + if (strcmp (section->name, name) == 0 && section->type == type) + return section; + } + + return NULL; +} + +static gboolean +parse_elf_signature (const guchar *data, + gsize length, + gboolean *is_64, + gboolean *is_be) +{ + /* FIXME: this function should be able to return an error */ + if (length < EI_NIDENT) + { + /* FIXME set error */ + return FALSE; + } + + if (data[EI_CLASS] != ELFCLASS32 && + data[EI_CLASS] != ELFCLASS64) + { + /* FIXME set error */ + return FALSE; + } + + if (data[EI_DATA] != ELFDATA2LSB && + data[EI_DATA] != ELFDATA2MSB) + { + /* FIXME set error */ + return FALSE; + } + + if (is_64) + *is_64 = (data[EI_CLASS] == ELFCLASS64); + + if (is_be) + *is_be = (data[EI_DATA] == ELFDATA2MSB); + + return TRUE; +} + +ElfParser * +elf_parser_new_from_data (const guchar *data, + gsize length) +{ + ElfParser *parser; + gboolean is_64, is_big_endian; + int section_names_idx; + const guchar *section_names; + G_GNUC_UNUSED gsize section_headers; + guint i; + + if (!parse_elf_signature (data, length, &is_64, &is_big_endian)) + { + /* FIXME: set error */ + return NULL; + } + + parser = g_new0 (ElfParser, 1); + + parser->is_64 = is_64; + parser->data = data; + parser->length = length; + +#if 0 + g_print (" new parser : %p\n", parser); +#endif + + /* Read ELF header */ + + parser->n_sections = e_shnum (parser); + section_names_idx = e_shstrndx (parser); + section_headers = e_shoff (parser); + + /* Read section headers */ + parser->sections = g_new0 (Section *, parser->n_sections); + + section_names = parser->data + sh_offset (parser, section_names_idx); + + for (i = 0; i < parser->n_sections; ++i) + { + Section *section = g_new (Section, 1); + + section->name = (char *)(section_names + sh_name (parser, i)); + section->size = sh_size (parser, i); + section->offset = sh_offset (parser, i); + section->allocated = !!(sh_flags (parser, i) & SHF_ALLOC); + + if (section->allocated) + section->load_address = sh_addr (parser, i); + else + section->load_address = 0; + + section->type = sh_type (parser, i); + + parser->sections[i] = section; + } + + /* Cache the text section */ + parser->text_section = find_section (parser, ".text", SHT_PROGBITS); + if (!parser->text_section) + parser->text_section = find_section (parser, ".text", SHT_NOBITS); + + parser->filename = NULL; + parser->build_id = NULL; + + return parser; +} + +static GMappedFile * +open_mapped_file (const char *filename, + GError **error) +{ + GMappedFile *file = NULL; + char *alternate = NULL; + + if (in_container () && !g_str_has_prefix (filename, g_get_home_dir ())) + { + alternate = g_build_filename ("/var/run/host", filename, NULL); + file = g_mapped_file_new (alternate, FALSE, NULL); + g_free (alternate); + } + + /* Flatpaks with filesystem=host don't have Silverblue's /sysroot in /var/run/host, + * yet they have it in /, which means the original path might work. + */ + if (!file) + file = g_mapped_file_new (filename, FALSE, error); + + return file; +} + +ElfParser * +elf_parser_new_from_mmap (GMappedFile *file, + GError **error) +{ + const guchar *data; + gsize length; + ElfParser *parser; + + if (file == NULL) + return NULL; + + data = (guchar *)g_mapped_file_get_contents (file); + length = g_mapped_file_get_length (file); + parser = elf_parser_new_from_data (data, length); + + if (!parser) + { + g_set_error (error, + G_FILE_ERROR, + G_FILE_ERROR_FAILED, + "Failed to load ELF from mmap region"); + g_mapped_file_unref (file); + return NULL; + } + + parser->filename = NULL; + parser->file = file; + + return parser; +} + +ElfParser * +elf_parser_new (const char *filename, + GError **error) +{ + GMappedFile *file; + const guchar *data; + gsize length; + ElfParser *parser; + + if (!(file = open_mapped_file (filename, error))) + return NULL; + +#if 0 + g_print ("elf parser new : %s\n", filename); +#endif + + data = (guchar *)g_mapped_file_get_contents (file); + length = g_mapped_file_get_length (file); + +#if 0 + g_print ("data %p: for %s\n", data, filename); +#endif + + parser = elf_parser_new_from_data (data, length); + +#if 0 + g_print ("Parser for %s: %p\n", filename, parser); +#endif + + if (!parser) + { + g_set_error (error, + G_FILE_ERROR, + G_FILE_ERROR_FAILED, + "Failed to load ELF from file %s", + filename); + g_mapped_file_unref (file); + return NULL; + } + + parser->filename = g_strdup (filename); + + parser->file = file; + +#if 0 + g_print ("Elf file: %s (debug: %s)\n", + filename, elf_parser_get_debug_link (parser, NULL)); + + if (!parser->symbols) + g_print ("at this point %s has no symbols\n", filename); +#endif + + return parser; +} + +guint32 +elf_parser_get_crc32 (ElfParser *parser) +{ + static const unsigned long crc32_table[256] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d + }; + const guchar *data; + gsize length; + gulong crc; + gsize i; + + data = parser->data; + length = parser->length; + + crc = 0xffffffff; + + madvise ((char *)data, length, MADV_SEQUENTIAL); + + for (i = 0; i < length; ++i) + crc = crc32_table[(crc ^ data[i]) & 0xff] ^ (crc >> 8); + + /* We just read the entire file into memory, but we only really + * need the symbol table, so swap the whole thing out. + * + * We could be more exact here, but it's only a few minor + * pagefaults. + */ + if (parser->file) + madvise ((char *)data, length, MADV_DONTNEED); + + return ~crc & 0xffffffff; +} + +void +elf_parser_free (ElfParser *parser) +{ + guint i; + + for (i = 0; i < parser->n_sections; ++i) + section_free (parser->sections[i]); + g_free (parser->sections); + + if (parser->file) + g_mapped_file_unref (parser->file); + + g_free (parser->symbols); + + if (parser->filename) + g_free (parser->filename); + + if (parser->build_id) + g_free (parser->build_id); + + g_free (parser); +} + +gchar * +elf_demangle (const char *name) +{ + gchar *demangled = sysprof_cplus_demangle (name); + + if (demangled) + return demangled; + else + return g_strdup (name); +} + +/* + * Looking up symbols + */ +static int +compare_sym (const void *a, const void *b) +{ + const ElfSym *sym_a = a; + const ElfSym *sym_b = b; + + if (sym_a->address < sym_b->address) + return -1; + else if (sym_a->address == sym_b->address) + return 0; + else + return 1; +} + +#if 0 +static void +dump_symbols (ElfParser *parser, ElfSym *syms, guint n_syms) +{ + int i; + + for (i = 0; i < n_syms; ++i) + { + ElfSym *s = &(syms[i]); + + g_print (" %s: %lx\n", elf_parser_get_sym_name (parser, s), s->address); + } +} +#endif + +static void +read_table (ElfParser *parser, + const Section *sym_table, + const Section *str_table) +{ + int sym_size = GET_SIZE (parser, Sym); + guint i, n_symbols; + +#if 0 + g_print ("elf: Reading table for %s\n", parser->filename? parser->filename : ""); +#endif + + parser->n_symbols = sym_table->size / sym_size; + parser->symbols = g_new (ElfSym, parser->n_symbols); + +#if 0 + g_print ("sym table offset: %d\n", sym_table->offset); +#endif + + n_symbols = 0; +#if 0 + g_print ("n syms: %d\n", parser->n_symbols); +#endif + for (i = 0; i < parser->n_symbols; ++i) + { + guint info; + gulong addr; + gulong shndx; + + info = st_info (parser, sym_table->offset, i); + addr = st_value (parser, sym_table->offset, i); + shndx = st_shndx (parser, sym_table->offset, i); + +#if 0 + g_print ("read symbol: %s (section: %d)\n", get_string_indirct (parser->parser, + parser->sym_format, "st_name", + str_table->offset), + shndx); +#endif + + if (addr != 0 && + shndx < parser->n_sections && + parser->sections[shndx] == parser->text_section && + (info & 0xf) == STT_FUNC && + ((info >> 4) == STB_GLOBAL || + (info >> 4) == STB_LOCAL || + (info >> 4) == STB_WEAK)) + { + parser->symbols[n_symbols].address = addr; + parser->symbols[n_symbols].table = sym_table->offset; + parser->symbols[n_symbols].offset = i; + + n_symbols++; + +#if 0 + g_print (" symbol: %s: %lx\n", + get_string_indirect (parser->parser, + parser->sym_format, "st_name", + str_table->offset), + addr - parser->text_section->load_address); + g_print (" sym %d in %p (info: %d:%d) (func:global %d:%d)\n", + addr, parser, info & 0xf, info >> 4, STT_FUNC, STB_GLOBAL); +#endif + } + else if (addr != 0) + { +#if 0 + g_print (" rejecting %d in %p (info: %d:%d) (func:global %d:%d)\n", + addr, parser, info & 0xf, info >> 4, STT_FUNC, STB_GLOBAL); +#endif + } + } + + parser->sym_strings = str_table->offset; + parser->n_symbols = n_symbols; + + /* Allocate space for at least one symbol, so that parser->symbols will be + * non-NULL. If it ends up being NULL, we will be parsing the file over and + * over. + */ + parser->symbols = g_renew (ElfSym, parser->symbols, parser->n_symbols + 1); + + qsort (parser->symbols, parser->n_symbols, sizeof (ElfSym), compare_sym); +} + +static void +read_symbols (ElfParser *parser) +{ + const Section *symtab = find_section (parser, ".symtab", SHT_SYMTAB); + const Section *strtab = find_section (parser, ".strtab", SHT_STRTAB); + const Section *dynsym = find_section (parser, ".dynsym", SHT_DYNSYM); + const Section *dynstr = find_section (parser, ".dynstr", SHT_STRTAB); + + if (symtab && strtab) + { +#if 0 + g_print ("reading symbol table of %s\n", parser->filename); +#endif + read_table (parser, symtab, strtab); + } + else if (dynsym && dynstr) + { +#if 0 + g_print ("reading dynamic symbol table of %s\n", parser->filename); +#endif + read_table (parser, dynsym, dynstr); + } + else + { + /* To make sure parser->symbols is non-NULL */ + parser->n_symbols = 0; + parser->symbols = g_new (ElfSym, 1); + } +} + +static ElfSym * +do_lookup (ElfSym *symbols, + gulong address, + int first, + int last) +{ + if (address >= symbols[last].address) + { + return &(symbols[last]); + } + else if (last - first < 3) + { + while (last >= first) + { + if (address >= symbols[last].address) + return &(symbols[last]); + + last--; + } + + return NULL; + } + else + { + int mid = (first + last) / 2; + + if (symbols[mid].address > address) + return do_lookup (symbols, address, first, mid); + else + return do_lookup (symbols, address, mid, last); + } +} + +/* Address should be given in 'offset into text segment' */ +const ElfSym * +elf_parser_lookup_symbol (ElfParser *parser, + gulong address) +{ + const ElfSym *result; + + if (!parser->symbols) + { +#if 0 + g_print ("reading symbols at %p\n", parser); +#endif + read_symbols (parser); + } + + if (parser->n_symbols == 0) + return NULL; + + if (!parser->text_section) + return NULL; + + address += parser->text_section->load_address; + +#if 0 + g_print ("elf: the address we are looking up is %p\n", address); +#endif + + result = do_lookup (parser->symbols, address, 0, parser->n_symbols - 1); + +#if 0 + if (result) + { + g_print (" elf: found %s at %lx\n", elf_parser_get_sym_name (parser, result), result->address); + } + else + { + g_print ("elf: not found\n"); + } +#endif + + if (result) + { + gulong size = st_size (parser, result->table, result->offset); + + if (size > 0 && result->address + size <= address) + { +#if 0 + g_print (" elf: ends at %lx, so rejecting\n", + result->address + size); +#endif + result = NULL; + } + } + + if (result) + { + /* Reject the symbols if the address is outside the text section */ + if (address > parser->text_section->load_address + parser->text_section->size) + result = NULL; + } + + return result; +} + +gulong +elf_parser_get_text_offset (ElfParser *parser) +{ + g_return_val_if_fail (parser != NULL, (gulong)-1); + + if (!parser->text_section) + return (gulong)-1; + + return parser->text_section->offset; +} + +static gchar * +make_hex_string (const guchar *data, int n_bytes) +{ + static const char hex_digits[] = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' + }; + GString *string = g_string_new (NULL); + int i; + + for (i = 0; i < n_bytes; ++i) + { + char c = data[i]; + + g_string_append_c (string, hex_digits[(c & 0xf0) >> 4]); + g_string_append_c (string, hex_digits[(c & 0x0f)]); + } + + return g_string_free (string, FALSE); +} + +const gchar * +elf_parser_get_build_id (ElfParser *parser) +{ + if (!parser->checked_build_id) + { + const Section *build_id = + find_section (parser, ".note.gnu.build-id", SHT_NOTE); + guint64 name_size; + guint64 desc_size; + guint64 type; + const char *name; + guint64 offset; + + parser->checked_build_id = TRUE; + + if (!build_id) + return NULL; + + offset = build_id->offset; + + name_size = GET_FIELD (parser, offset, Nhdr, 0, n_namesz); + desc_size = GET_FIELD (parser, offset, Nhdr, 0, n_descsz); + type = GET_FIELD (parser, offset, Nhdr, 0, n_type); + + offset += GET_SIZE (parser, Nhdr); + + name = (char *)(parser->data + offset); + + if (strncmp (name, ELF_NOTE_GNU, name_size) != 0 || type != NT_GNU_BUILD_ID) + return NULL; + + offset += strlen (name); + + offset = (offset + 3) & (~0x3); + + parser->build_id = make_hex_string (parser->data + offset, desc_size); + } + + return parser->build_id; +} + +const char * +elf_parser_get_debug_link (ElfParser *parser, + guint32 *crc32) +{ + guint64 offset; + const Section *debug_link = find_section (parser, ".gnu_debuglink", + SHT_PROGBITS); + const gchar *result; + + if (!debug_link) + return NULL; + + offset = debug_link->offset; + + result = (char *)(parser->data + offset); + + if (crc32) + { + int len = strlen (result) + 1; + offset = (offset + len + 3) & ~0x3; + + *crc32 = GET_UINT32 (parser, offset); + } + + return result; +} + +static const guchar * +get_section (ElfParser *parser, + const char *name) +{ + const Section *section = find_section (parser, name, SHT_PROGBITS); + + if (section) + return parser->data + section->offset; + else + return NULL; +} + +const guchar * +elf_parser_get_eh_frame (ElfParser *parser) +{ + return get_section (parser, ".eh_frame"); +} + +const guchar * +elf_parser_get_debug_frame (ElfParser *parser) +{ + return get_section (parser, ".debug_frame"); +} + +const char * +elf_parser_get_sym_name (ElfParser *parser, + const ElfSym *sym) +{ + g_return_val_if_fail (parser != NULL, NULL); + + return (char *)(parser->data + parser->sym_strings + + st_name (parser, sym->table, sym->offset)); +} + +gboolean +elf_parser_owns_symbol (ElfParser *parser, + const ElfSym *sym) +{ + ElfSym *first, *last; + + if (!parser->n_symbols) + return FALSE; + + first = parser->symbols; + last = parser->symbols + parser->n_symbols - 1; + + return first <= sym && sym <= last; +} + +gulong +elf_parser_get_sym_address (ElfParser *parser, + const ElfSym *sym) +{ + return sym->address - parser->text_section->load_address; +} + +void +elf_parser_get_sym_address_range (ElfParser *parser, + const ElfSym *sym, + gulong *begin, + gulong *end) +{ + *begin = sym->address - parser->text_section->load_address; + *end = *begin + st_size (parser, sym->table, sym->offset); +} diff --git a/contrib/elfparser/elfparser.h b/contrib/elfparser/elfparser.h new file mode 100644 index 00000000..fd19e78d --- /dev/null +++ b/contrib/elfparser/elfparser.h @@ -0,0 +1,68 @@ +/* Sysprof -- Sampling, systemwide CPU profiler + * Copyright 2006, 2007, Soeren Sandmann + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include + +#ifndef NT_GNU_BUILD_ID +#define NT_GNU_BUILD_ID 3 +#endif + +typedef struct ElfSym ElfSym; +typedef struct ElfParser ElfParser; + +ElfParser *elf_parser_new_from_data (const guchar *data, + gsize length); +ElfParser *elf_parser_new_from_mmap (GMappedFile *mapped_file, + GError **err); +ElfParser *elf_parser_new (const char *filename, + GError **err); +void elf_parser_free (ElfParser *parser); +const char *elf_parser_get_debug_link (ElfParser *parser, + guint32 *crc32); +const gchar *elf_parser_get_build_id (ElfParser *parser); +const guchar *elf_parser_get_eh_frame (ElfParser *parser); +const guchar *elf_parser_get_debug_frame (ElfParser *parser); +gulong elf_parser_get_text_offset (ElfParser *parser); + + +/* Lookup a symbol in the file. + * + * The symbol returned is const, so don't free it. It is valid until + * elf_parser_free() is called on the parser. + * + * The address should be given in "file coordinates". This means that + * if the file is mapped at address m and offset o, then an address a + * should be looked up as "a - (m - o)". (m - o) is where the start + * of the file would have been mapped, so a - (m - o) is the position + * in the file of a. + */ +const ElfSym *elf_parser_lookup_symbol (ElfParser *parser, + gulong address); +guint32 elf_parser_get_crc32 (ElfParser *parser); +const char *elf_parser_get_sym_name (ElfParser *parser, + const ElfSym *sym); +gulong elf_parser_get_sym_address (ElfParser *parser, + const ElfSym *sym); +gboolean elf_parser_owns_symbol (ElfParser *parser, + const ElfSym *sym); +char *elf_demangle (const char *name); +void elf_parser_get_sym_address_range (ElfParser *parser, + const ElfSym *sym, + gulong *begin, + gulong *end); + diff --git a/contrib/elfparser/meson.build b/contrib/elfparser/meson.build new file mode 100644 index 00000000..b3c75e6b --- /dev/null +++ b/contrib/elfparser/meson.build @@ -0,0 +1,19 @@ +libelfparser_sources = [ + 'demangle.cpp', + 'elfparser.c', +] + +libelfparser_deps = [ + dependency('glib-2.0', version: glib_req_version), +] + +libelfparser_static = static_library('elfparser', libelfparser_sources, + dependencies: libelfparser_deps, + gnu_symbol_visibility: 'hidden', +) + +libelfparser_static_dep = declare_dependency( + include_directories: include_directories('.'), + dependencies: libelfparser_deps, + link_with: libelfparser_static, +) diff --git a/contrib/meson.build b/contrib/meson.build new file mode 100644 index 00000000..691bcd0d --- /dev/null +++ b/contrib/meson.build @@ -0,0 +1 @@ +subdir('elfparser') diff --git a/meson.build b/meson.build index e9e22034..320bbe79 100644 --- a/meson.build +++ b/meson.build @@ -238,6 +238,7 @@ endif needs_service_access = get_option('libsysprof') or get_option('agent') install_service_files = needs_service_access or get_option('sysprofd') == 'bundled' +subdir('contrib') subdir('src') subdir('data') subdir('po') diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build index d8d25a12..d126823e 100644 --- a/src/libsysprof-analyze/meson.build +++ b/src/libsysprof-analyze/meson.build @@ -33,9 +33,6 @@ libsysprof_analyze_public_sources = [ ] libsysprof_analyze_private_sources = [ - '../libsysprof/binfile.c', - '../libsysprof/demangle.cpp', - '../libsysprof/elfparser.c', 'sysprof-address-layout.c', 'sysprof-document-bitset-index.c', 'sysprof-document-symbols.c', @@ -89,6 +86,7 @@ libsysprof_analyze_deps = [ dependency('gio-2.0', version: glib_req_version), dependency('gtk4', version: gtk_req_version), + libelfparser_static_dep, libsysprof_capture_dep, ] From 375aaf70861eee71701af232f52b03f8c919fea8 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 25 May 2023 15:10:35 -0700 Subject: [PATCH 0261/1030] contrib: import GtkBitset as EggBitset This imports GTK's GtkBitset and the underlying roaring bitmaps so that we will have it without having to use GTK from libsysprof-analyze. --- contrib/eggbitset/COPYING | 202 + contrib/eggbitset/README.md | 16 + contrib/eggbitset/eggbitset.c | 984 +++ contrib/eggbitset/eggbitset.h | 137 + contrib/eggbitset/meson.build | 17 + contrib/eggbitset/roaring.c | 11475 ++++++++++++++++++++++++++++++++ contrib/eggbitset/roaring.h | 7090 ++++++++++++++++++++ contrib/meson.build | 1 + 8 files changed, 19922 insertions(+) create mode 100644 contrib/eggbitset/COPYING create mode 100644 contrib/eggbitset/README.md create mode 100644 contrib/eggbitset/eggbitset.c create mode 100644 contrib/eggbitset/eggbitset.h create mode 100644 contrib/eggbitset/meson.build create mode 100644 contrib/eggbitset/roaring.c create mode 100644 contrib/eggbitset/roaring.h diff --git a/contrib/eggbitset/COPYING b/contrib/eggbitset/COPYING new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/contrib/eggbitset/COPYING @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/contrib/eggbitset/README.md b/contrib/eggbitset/README.md new file mode 100644 index 00000000..9404a84b --- /dev/null +++ b/contrib/eggbitset/README.md @@ -0,0 +1,16 @@ +Roaring bitmaps implementation +============================== + +This directory contains code modified for GTK, based on the Roaring +bitmaps reference implementation +[CRoaring](https://github.com/RoaringBitmap/CRoaring). + +It is not necessarily compatible with past or future versions of CRoaring, +and replacing it with a different version or linking to a system copy +is not supported. + +See the source files for copyright and licensing information, and the +`COPYING` file for the full text of the Apache license, version 2.0. + +When proposing modifications for these files, please consider whether they +are also suitable for submission to CRoaring. diff --git a/contrib/eggbitset/eggbitset.c b/contrib/eggbitset/eggbitset.c new file mode 100644 index 00000000..f1d45d6f --- /dev/null +++ b/contrib/eggbitset/eggbitset.c @@ -0,0 +1,984 @@ +/* + * Copyright © 2020 Benjamin Otte + * + * 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 Lesser General Public + * License along with this library. If not, see . + * + * Authors: Benjamin Otte + */ + +#include "config.h" + +#include "eggbitset.h" + +#include "roaring.c" + +/** + * EggBitset: (ref-func egg_bitset_ref) (unref-func egg_bitset_unref) + * + * A `EggBitset` represents a set of unsigned integers. + * + * Another name for this data structure is "bitmap". + * + * The current implementation is based on [roaring bitmaps](https://roaringbitmap.org/). + * + * A bitset allows adding a set of integers and provides support for set operations + * like unions, intersections and checks for equality or if a value is contained + * in the set. `EggBitset` also contains various functions to query metadata about + * the bitset, such as the minimum or maximum values or its size. + * + * The fastest way to iterate values in a bitset is [struct@Egg.BitsetIter]. + * + * The main use case for `EggBitset` is implementing complex selections for + * [iface@Egg.SelectionModel]. + */ + +struct _EggBitset +{ + int ref_count; + roaring_bitmap_t roaring; +}; + + +G_DEFINE_BOXED_TYPE (EggBitset, egg_bitset, + egg_bitset_ref, + egg_bitset_unref) + +/** + * egg_bitset_ref: + * @self: (nullable): a `EggBitset` + * + * Acquires a reference on the given `EggBitset`. + * + * Returns: (transfer none): the `EggBitset` with an additional reference + */ +EggBitset * +egg_bitset_ref (EggBitset *self) +{ + g_return_val_if_fail (self != NULL, NULL); + + self->ref_count += 1; + + return self; +} + +/** + * egg_bitset_unref: + * @self: (nullable): a `EggBitset` + * + * Releases a reference on the given `EggBitset`. + * + * If the reference was the last, the resources associated to the @self are + * freed. + */ +void +egg_bitset_unref (EggBitset *self) +{ + g_return_if_fail (self != NULL); + g_return_if_fail (self->ref_count > 0); + + self->ref_count -= 1; + if (self->ref_count > 0) + return; + + ra_clear (&self->roaring.high_low_container); + g_free (self); +} + +/** + * egg_bitset_contains: + * @self: a `EggBitset` + * @value: the value to check + * + * Checks if the given @value has been added to @self + * + * Returns: %TRUE if @self contains @value + **/ +gboolean +egg_bitset_contains (const EggBitset *self, + guint value) +{ + g_return_val_if_fail (self != NULL, FALSE); + + return roaring_bitmap_contains (&self->roaring, value); +} + +/** + * egg_bitset_is_empty: + * @self: a `EggBitset` + * + * Check if no value is contained in bitset. + * + * Returns: %TRUE if @self is empty + **/ +gboolean +egg_bitset_is_empty (const EggBitset *self) +{ + g_return_val_if_fail (self != NULL, TRUE); + + return roaring_bitmap_is_empty (&self->roaring); +} + +/** + * egg_bitset_equals: + * @self: a `EggBitset` + * @other: another `EggBitset` + * + * Returns %TRUE if @self and @other contain the same values. + * + * Returns: %TRUE if @self and @other contain the same values + **/ +gboolean +egg_bitset_equals (const EggBitset *self, + const EggBitset *other) +{ + g_return_val_if_fail (self != NULL, other == NULL); + g_return_val_if_fail (other != NULL, FALSE); + + if (self == other) + return TRUE; + + return roaring_bitmap_equals (&self->roaring, &other->roaring); +} + +/** + * egg_bitset_get_minimum: + * @self: a `EggBitset` + * + * Returns the smallest value in @self. + * + * If @self is empty, `G_MAXUINT` is returned. + * + * Returns: The smallest value in @self + **/ +guint +egg_bitset_get_minimum (const EggBitset *self) +{ + g_return_val_if_fail (self != NULL, G_MAXUINT); + + return roaring_bitmap_minimum (&self->roaring); +} + +/** + * egg_bitset_get_maximum: + * @self: a `EggBitset` + * + * Returns the largest value in @self. + * + * If @self is empty, 0 is returned. + * + * Returns: The largest value in @self + **/ +guint +egg_bitset_get_maximum (const EggBitset *self) +{ + g_return_val_if_fail (self != NULL, 0); + + return roaring_bitmap_maximum (&self->roaring); +} + +/** + * egg_bitset_get_size: + * @self: a `EggBitset` + * + * Gets the number of values that were added to the set. + * + * For example, if the set is empty, 0 is returned. + * + * Note that this function returns a `guint64`, because when all + * values are set, the return value is `G_MAXUINT + 1`. Unless you + * are sure this cannot happen (it can't with `GListModel`), be sure + * to use a 64bit type. + * + * Returns: The number of values in the set. + */ +guint64 +egg_bitset_get_size (const EggBitset *self) +{ + g_return_val_if_fail (self != NULL, 0); + + return roaring_bitmap_get_cardinality (&self->roaring); +} + +/** + * egg_bitset_get_size_in_range: + * @self: a `EggBitset` + * @first: the first element to include + * @last: the last element to include + * + * Gets the number of values that are part of the set from @first to @last + * (inclusive). + * + * Note that this function returns a `guint64`, because when all values are + * set, the return value is `G_MAXUINT + 1`. Unless you are sure this cannot + * happen (it can't with `GListModel`), be sure to use a 64bit type. + * + * Returns: The number of values in the set from @first to @last. + */ +guint64 +egg_bitset_get_size_in_range (const EggBitset *self, + guint first, + guint last) +{ + g_return_val_if_fail (self != NULL, 0); + g_return_val_if_fail (last >= first, 0); + + return roaring_bitmap_range_cardinality (&self->roaring, first, ((uint64_t) last) + 1); +} + +/** + * egg_bitset_get_nth: + * @self: a `EggBitset` + * @nth: index of the item to get + * + * Returns the value of the @nth item in self. + * + * If @nth is >= the size of @self, 0 is returned. + * + * Returns: the value of the @nth item in @self + */ +guint +egg_bitset_get_nth (const EggBitset *self, + guint nth) +{ + uint32_t result; + + if (!roaring_bitmap_select (&self->roaring, nth, &result)) + return 0; + + return result; +} + +/** + * egg_bitset_new_empty: + * + * Creates a new empty bitset. + * + * Returns: A new empty bitset + */ +EggBitset * +egg_bitset_new_empty (void) +{ + EggBitset *self; + + self = g_new0 (EggBitset, 1); + + self->ref_count = 1; + + ra_init (&self->roaring.high_low_container); + + return self; +} + +/** + * egg_bitset_new_range: + * @start: first value to add + * @n_items: number of consecutive values to add + * + * Creates a bitset with the given range set. + * + * Returns: A new bitset + **/ +EggBitset * +egg_bitset_new_range (guint start, + guint n_items) +{ + EggBitset *self; + + self = egg_bitset_new_empty (); + + egg_bitset_add_range (self, start, n_items); + + return self; +} + +/** + * egg_bitset_copy: + * @self: a `EggBitset` + * + * Creates a copy of @self. + * + * Returns: (transfer full): A new bitset that contains the same + * values as @self + */ +EggBitset * +egg_bitset_copy (const EggBitset *self) +{ + EggBitset *copy; + + g_return_val_if_fail (self != NULL, NULL); + + copy = egg_bitset_new_empty (); + roaring_bitmap_overwrite (©->roaring, &self->roaring); + + return copy; +} + +/** + * egg_bitset_remove_all: + * @self: a `EggBitset` + * + * Removes all values from the bitset so that it is empty again. + */ +void +egg_bitset_remove_all (EggBitset *self) +{ + g_return_if_fail (self != NULL); + + roaring_bitmap_clear (&self->roaring); +} + +/** + * egg_bitset_add: + * @self: a `EggBitset` + * @value: value to add + * + * Adds @value to @self if it wasn't part of it before. + * + * Returns: %TRUE if @value was not part of @self and @self + * was changed + */ +gboolean +egg_bitset_add (EggBitset *self, + guint value) +{ + g_return_val_if_fail (self != NULL, FALSE); + + return roaring_bitmap_add_checked (&self->roaring, value); +} + +/** + * egg_bitset_remove: + * @self: a `EggBitset` + * @value: value to remove + * + * Removes @value from @self if it was part of it before. + * + * Returns: %TRUE if @value was part of @self and @self + * was changed + */ +gboolean +egg_bitset_remove (EggBitset *self, + guint value) +{ + g_return_val_if_fail (self != NULL, FALSE); + + return roaring_bitmap_remove_checked (&self->roaring, value); +} + +/** + * egg_bitset_add_range: + * @self: a `EggBitset` + * @start: first value to add + * @n_items: number of consecutive values to add + * + * Adds all values from @start (inclusive) to @start + @n_items + * (exclusive) in @self. + */ +void +egg_bitset_add_range (EggBitset *self, + guint start, + guint n_items) +{ + g_return_if_fail (self != NULL); + + if (n_items == 0) + return; + + /* overflow check, the == 0 is to allow add_range(G_MAXUINT, 1); */ + g_return_if_fail (start + n_items == 0 || start + n_items > start); + + roaring_bitmap_add_range_closed (&self->roaring, start, start + n_items - 1); +} + +/** + * egg_bitset_remove_range: + * @self: a `EggBitset` + * @start: first value to remove + * @n_items: number of consecutive values to remove + * + * Removes all values from @start (inclusive) to @start + @n_items (exclusive) + * in @self. + */ +void +egg_bitset_remove_range (EggBitset *self, + guint start, + guint n_items) +{ + g_return_if_fail (self != NULL); + + if (n_items == 0) + return; + + /* overflow check, the == 0 is to allow add_range(G_MAXUINT, 1); */ + g_return_if_fail (start + n_items == 0 || start + n_items > start); + + roaring_bitmap_remove_range_closed (&self->roaring, start, start + n_items - 1); +} + +/** + * egg_bitset_add_range_closed: + * @self: a `EggBitset` + * @first: first value to add + * @last: last value to add + * + * Adds the closed range [@first, @last], so @first, @last and all + * values in between. @first must be smaller than @last. + */ +void +egg_bitset_add_range_closed (EggBitset *self, + guint first, + guint last) +{ + g_return_if_fail (self != NULL); + g_return_if_fail (first <= last); + + roaring_bitmap_add_range_closed (&self->roaring, first, last); +} + +/** + * egg_bitset_remove_range_closed: + * @self: a `EggBitset` + * @first: first value to remove + * @last: last value to remove + * + * Removes the closed range [@first, @last], so @first, @last and all + * values in between. @first must be smaller than @last. + */ +void +egg_bitset_remove_range_closed (EggBitset *self, + guint first, + guint last) +{ + g_return_if_fail (self != NULL); + g_return_if_fail (first <= last); + + roaring_bitmap_remove_range_closed (&self->roaring, first, last); +} + +/** + * egg_bitset_add_rectangle: + * @self: a `EggBitset` + * @start: first value to add + * @width: width of the rectangle + * @height: height of the rectangle + * @stride: row stride of the grid + * + * Interprets the values as a 2-dimensional boolean grid with the given @stride + * and inside that grid, adds a rectangle with the given @width and @height. + */ +void +egg_bitset_add_rectangle (EggBitset *self, + guint start, + guint width, + guint height, + guint stride) +{ + guint i; + + g_return_if_fail (self != NULL); + g_return_if_fail ((start % stride) + width <= stride); + g_return_if_fail (G_MAXUINT - start >= height * stride); + + if (width == 0 || height == 0) + return; + + for (i = 0; i < height; i++) + egg_bitset_add_range (self, i * stride + start, width); +} + +/** + * egg_bitset_remove_rectangle: + * @self: a `EggBitset` + * @start: first value to remove + * @width: width of the rectangle + * @height: height of the rectangle + * @stride: row stride of the grid + * + * Interprets the values as a 2-dimensional boolean grid with the given @stride + * and inside that grid, removes a rectangle with the given @width and @height. + */ +void +egg_bitset_remove_rectangle (EggBitset *self, + guint start, + guint width, + guint height, + guint stride) +{ + guint i; + + g_return_if_fail (self != NULL); + g_return_if_fail (width <= stride); + g_return_if_fail (G_MAXUINT - start >= height * stride); + + if (width == 0 || height == 0) + return; + + for (i = 0; i < height; i++) + egg_bitset_remove_range (self, i * stride + start, width); +} + +/** + * egg_bitset_union: + * @self: a `EggBitset` + * @other: the `EggBitset` to union with + * + * Sets @self to be the union of @self and @other. + * + * That is, add all values from @other into @self that weren't part of it. + * + * It is allowed for @self and @other to be the same bitset. Nothing will + * happen in that case. + */ +void +egg_bitset_union (EggBitset *self, + const EggBitset *other) +{ + g_return_if_fail (self != NULL); + g_return_if_fail (other != NULL); + + if (self == other) + return; + + roaring_bitmap_or_inplace (&self->roaring, &other->roaring); +} + +/** + * egg_bitset_intersect: + * @self: a `EggBitset` + * @other: the `EggBitset` to intersect with + * + * Sets @self to be the intersection of @self and @other. + * + * In other words, remove all values from @self that are not part of @other. + * + * It is allowed for @self and @other to be the same bitset. Nothing will + * happen in that case. + */ +void +egg_bitset_intersect (EggBitset *self, + const EggBitset *other) +{ + g_return_if_fail (self != NULL); + g_return_if_fail (other != NULL); + + if (self == other) + return; + + roaring_bitmap_and_inplace (&self->roaring, &other->roaring); +} + +/** + * egg_bitset_subtract: + * @self: a `EggBitset` + * @other: the `EggBitset` to subtract + * + * Sets @self to be the subtraction of @other from @self. + * + * In other words, remove all values from @self that are part of @other. + * + * It is allowed for @self and @other to be the same bitset. The bitset + * will be emptied in that case. + */ +void +egg_bitset_subtract (EggBitset *self, + const EggBitset *other) +{ + g_return_if_fail (self != NULL); + g_return_if_fail (other != NULL); + + if (self == other) + { + roaring_bitmap_clear (&self->roaring); + return; + } + + roaring_bitmap_andnot_inplace (&self->roaring, &other->roaring); +} + +/** + * egg_bitset_difference: + * @self: a `EggBitset` + * @other: the `EggBitset` to compute the difference from + * + * Sets @self to be the symmetric difference of @self and @other. + * + * The symmetric difference is set @self to contain all values that + * were either contained in @self or in @other, but not in both. + * This operation is also called an XOR. + * + * It is allowed for @self and @other to be the same bitset. The bitset + * will be emptied in that case. + */ +void +egg_bitset_difference (EggBitset *self, + const EggBitset *other) +{ + g_return_if_fail (self != NULL); + g_return_if_fail (other != NULL); + + if (self == other) + { + roaring_bitmap_clear (&self->roaring); + return; + } + + roaring_bitmap_xor_inplace (&self->roaring, &other->roaring); +} + +/** + * egg_bitset_shift_left: + * @self: a `EggBitset` + * @amount: amount to shift all values to the left + * + * Shifts all values in @self to the left by @amount. + * + * Values smaller than @amount are discarded. + */ +void +egg_bitset_shift_left (EggBitset *self, + guint amount) +{ + EggBitset *original; + EggBitsetIter iter; + guint value; + gboolean loop; + + g_return_if_fail (self != NULL); + + if (amount == 0) + return; + + original = egg_bitset_copy (self); + egg_bitset_remove_all (self); + + for (loop = egg_bitset_iter_init_at (&iter, original, amount, &value); + loop; + loop = egg_bitset_iter_next (&iter, &value)) + { + egg_bitset_add (self, value - amount); + } + + egg_bitset_unref (original); +} + +/** + * egg_bitset_shift_right: + * @self: a `EggBitset` + * @amount: amount to shift all values to the right + * + * Shifts all values in @self to the right by @amount. + * + * Values that end up too large to be held in a #guint are discarded. + */ +void +egg_bitset_shift_right (EggBitset *self, + guint amount) +{ + EggBitset *original; + EggBitsetIter iter; + guint value; + gboolean loop; + + g_return_if_fail (self != NULL); + + if (amount == 0) + return; + + original = egg_bitset_copy (self); + egg_bitset_remove_all (self); + + for (loop = egg_bitset_iter_init_first (&iter, original, &value); + loop && value <= G_MAXUINT - amount; + loop = egg_bitset_iter_next (&iter, &value)) + { + egg_bitset_add (self, value + amount); + } + + egg_bitset_unref (original); +} + +/** + * egg_bitset_splice: + * @self: a `EggBitset` + * @position: position at which to slice + * @removed: number of values to remove + * @added: number of values to add + * + * This is a support function for `GListModel` handling, by mirroring + * the `GlistModel::items-changed` signal. + * + * First, it "cuts" the values from @position to @removed from + * the bitset. That is, it removes all those values and shifts + * all larger values to the left by @removed places. + * + * Then, it "pastes" new room into the bitset by shifting all values + * larger than @position by @added spaces to the right. This frees + * up space that can then be filled. + */ +void +egg_bitset_splice (EggBitset *self, + guint position, + guint removed, + guint added) +{ + g_return_if_fail (self != NULL); + /* overflow */ + g_return_if_fail (position + removed >= position); + g_return_if_fail (position + added >= position); + + egg_bitset_remove_range (self, position, removed); + + if (removed != added) + { + EggBitset *shift = egg_bitset_copy (self); + + egg_bitset_remove_range (shift, 0, position); + egg_bitset_remove_range_closed (self, position, G_MAXUINT); + if (added > removed) + egg_bitset_shift_right (shift, added - removed); + else + egg_bitset_shift_left (shift, removed - added); + egg_bitset_union (self, shift); + egg_bitset_unref (shift); + } +} + +G_STATIC_ASSERT (sizeof (EggBitsetIter) >= sizeof (roaring_uint32_iterator_t)); + +static EggBitsetIter * +egg_bitset_iter_copy (EggBitsetIter *iter) +{ + roaring_uint32_iterator_t *riter = (roaring_uint32_iterator_t *) iter; + + return (EggBitsetIter *) roaring_copy_uint32_iterator (riter); +} + +static void +egg_bitset_iter_free (EggBitsetIter *iter) +{ + roaring_uint32_iterator_t *riter = (roaring_uint32_iterator_t *) iter; + + roaring_free_uint32_iterator (riter); +} + +G_DEFINE_BOXED_TYPE (EggBitsetIter, egg_bitset_iter, egg_bitset_iter_copy, egg_bitset_iter_free) + +/** + * egg_bitset_iter_init_first: + * @iter: (out): a pointer to an uninitialized `EggBitsetIter` + * @set: a `EggBitset` + * @value: (out) (optional): Set to the first value in @set + * + * Initializes an iterator for @set and points it to the first + * value in @set. + * + * If @set is empty, %FALSE is returned and @value is set to %G_MAXUINT. + * + * Returns: %TRUE if @set isn't empty. + */ +gboolean +egg_bitset_iter_init_first (EggBitsetIter *iter, + const EggBitset *set, + guint *value) +{ + roaring_uint32_iterator_t *riter = (roaring_uint32_iterator_t *) iter; + + g_return_val_if_fail (iter != NULL, FALSE); + g_return_val_if_fail (set != NULL, FALSE); + + roaring_init_iterator (&set->roaring, riter); + + if (value) + *value = riter->has_value ? riter->current_value : 0; + + return riter->has_value; +} + +/** + * egg_bitset_iter_init_last: + * @iter: (out): a pointer to an uninitialized `EggBitsetIter` + * @set: a `EggBitset` + * @value: (out) (optional): Set to the last value in @set + * + * Initializes an iterator for @set and points it to the last + * value in @set. + * + * If @set is empty, %FALSE is returned. + * + * Returns: %TRUE if @set isn't empty. + **/ +gboolean +egg_bitset_iter_init_last (EggBitsetIter *iter, + const EggBitset *set, + guint *value) +{ + roaring_uint32_iterator_t *riter = (roaring_uint32_iterator_t *) iter; + + g_return_val_if_fail (iter != NULL, FALSE); + g_return_val_if_fail (set != NULL, FALSE); + + roaring_init_iterator_last (&set->roaring, riter); + + if (value) + *value = riter->has_value ? riter->current_value : 0; + + return riter->has_value; +} + +/** + * egg_bitset_iter_init_at: + * @iter: (out): a pointer to an uninitialized `EggBitsetIter` + * @set: a `EggBitset` + * @target: target value to start iterating at + * @value: (out) (optional): Set to the found value in @set + * + * Initializes @iter to point to @target. + * + * If @target is not found, finds the next value after it. + * If no value >= @target exists in @set, this function returns %FALSE. + * + * Returns: %TRUE if a value was found. + */ +gboolean +egg_bitset_iter_init_at (EggBitsetIter *iter, + const EggBitset *set, + guint target, + guint *value) +{ + roaring_uint32_iterator_t *riter = (roaring_uint32_iterator_t *) iter; + + g_return_val_if_fail (iter != NULL, FALSE); + g_return_val_if_fail (set != NULL, FALSE); + + roaring_init_iterator (&set->roaring, riter); + if (!roaring_move_uint32_iterator_equalorlarger (riter, target)) + { + if (value) + *value = 0; + return FALSE; + } + + if (value) + *value = riter->current_value; + + return TRUE; +} + +/** + * egg_bitset_iter_next: + * @iter: a pointer to a valid `EggBitsetIter` + * @value: (out) (optional): Set to the next value + * + * Moves @iter to the next value in the set. + * + * If it was already pointing to the last value in the set, + * %FALSE is returned and @iter is invalidated. + * + * Returns: %TRUE if a next value existed + */ +gboolean +egg_bitset_iter_next (EggBitsetIter *iter, + guint *value) +{ + roaring_uint32_iterator_t *riter = (roaring_uint32_iterator_t *) iter; + + g_return_val_if_fail (iter != NULL, FALSE); + + if (!roaring_advance_uint32_iterator (riter)) + { + if (value) + *value = 0; + return FALSE; + } + + if (value) + *value = riter->current_value; + + return TRUE; +} + +/** + * egg_bitset_iter_previous: + * @iter: a pointer to a valid `EggBitsetIter` + * @value: (out) (optional): Set to the previous value + * + * Moves @iter to the previous value in the set. + * + * If it was already pointing to the first value in the set, + * %FALSE is returned and @iter is invalidated. + * + * Returns: %TRUE if a previous value existed + */ +gboolean +egg_bitset_iter_previous (EggBitsetIter *iter, + guint *value) +{ + roaring_uint32_iterator_t *riter = (roaring_uint32_iterator_t *) iter; + + g_return_val_if_fail (iter != NULL, FALSE); + + if (!roaring_previous_uint32_iterator (riter)) + { + if (value) + *value = 0; + return FALSE; + } + + if (value) + *value = riter->current_value; + + return TRUE; +} + +/** + * egg_bitset_iter_get_value: + * @iter: a `EggBitsetIter` + * + * Gets the current value that @iter points to. + * + * If @iter is not valid and [method@Egg.BitsetIter.is_valid] + * returns %FALSE, this function returns 0. + * + * Returns: The current value pointer to by @iter + */ +guint +egg_bitset_iter_get_value (const EggBitsetIter *iter) +{ + roaring_uint32_iterator_t *riter = (roaring_uint32_iterator_t *) iter; + + g_return_val_if_fail (iter != NULL, 0); + + if (!riter->has_value) + return 0; + + return riter->current_value; +} + +/** + * egg_bitset_iter_is_valid: + * @iter: a `EggBitsetIter` + * + * Checks if @iter points to a valid value. + * + * Returns: %TRUE if @iter points to a valid value + */ +gboolean +egg_bitset_iter_is_valid (const EggBitsetIter *iter) +{ + roaring_uint32_iterator_t *riter = (roaring_uint32_iterator_t *) iter; + + g_return_val_if_fail (iter != NULL, FALSE); + + return riter->has_value; +} diff --git a/contrib/eggbitset/eggbitset.h b/contrib/eggbitset/eggbitset.h new file mode 100644 index 00000000..3127ce2c --- /dev/null +++ b/contrib/eggbitset/eggbitset.h @@ -0,0 +1,137 @@ +/* + * Copyright © 2020 Benjamin Otte + * + * 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 Lesser General Public + * License along with this library. If not, see . + * + * Authors: Benjamin Otte + */ + + +#pragma once + +#include + +G_BEGIN_DECLS + +#define EGG_TYPE_BITSET (egg_bitset_get_type ()) + +typedef struct _EggBitset EggBitset; + +GType egg_bitset_get_type (void) G_GNUC_CONST; +EggBitset * egg_bitset_ref (EggBitset *self); +void egg_bitset_unref (EggBitset *self); +G_DEFINE_AUTOPTR_CLEANUP_FUNC(EggBitset, egg_bitset_unref) + +gboolean egg_bitset_contains (const EggBitset *self, + guint value); +gboolean egg_bitset_is_empty (const EggBitset *self); +gboolean egg_bitset_equals (const EggBitset *self, + const EggBitset *other); +guint64 egg_bitset_get_size (const EggBitset *self); +guint64 egg_bitset_get_size_in_range (const EggBitset *self, + guint first, + guint last); +guint egg_bitset_get_nth (const EggBitset *self, + guint nth); +guint egg_bitset_get_minimum (const EggBitset *self); +guint egg_bitset_get_maximum (const EggBitset *self); + +EggBitset * egg_bitset_new_empty (void); +EggBitset * egg_bitset_copy (const EggBitset *self); +EggBitset * egg_bitset_new_range (guint start, + guint n_items); + +void egg_bitset_remove_all (EggBitset *self); +gboolean egg_bitset_add (EggBitset *self, + guint value); +gboolean egg_bitset_remove (EggBitset *self, + guint value); +void egg_bitset_add_range (EggBitset *self, + guint start, + guint n_items); +void egg_bitset_remove_range (EggBitset *self, + guint start, + guint n_items); +void egg_bitset_add_range_closed (EggBitset *self, + guint first, + guint last); +void egg_bitset_remove_range_closed (EggBitset *self, + guint first, + guint last); +void egg_bitset_add_rectangle (EggBitset *self, + guint start, + guint width, + guint height, + guint stride); +void egg_bitset_remove_rectangle (EggBitset *self, + guint start, + guint width, + guint height, + guint stride); + +void egg_bitset_union (EggBitset *self, + const EggBitset *other); +void egg_bitset_intersect (EggBitset *self, + const EggBitset *other); +void egg_bitset_subtract (EggBitset *self, + const EggBitset *other); +void egg_bitset_difference (EggBitset *self, + const EggBitset *other); +void egg_bitset_shift_left (EggBitset *self, + guint amount); +void egg_bitset_shift_right (EggBitset *self, + guint amount); +void egg_bitset_splice (EggBitset *self, + guint position, + guint removed, + guint added); + +/** + * EggBitsetIter: + * + * An opaque, stack-allocated struct for iterating + * over the elements of a `EggBitset`. + * + * Before a `EggBitsetIter` can be used, it needs to be initialized with + * [func@Egg.BitsetIter.init_first], [func@Egg.BitsetIter.init_last] + * or [func@Egg.BitsetIter.init_at]. + */ +typedef struct _EggBitsetIter EggBitsetIter; + +struct _EggBitsetIter +{ + /*< private >*/ + gpointer private_data[10]; +}; + +GType egg_bitset_iter_get_type (void) G_GNUC_CONST; +gboolean egg_bitset_iter_init_first (EggBitsetIter *iter, + const EggBitset *set, + guint *value); +gboolean egg_bitset_iter_init_last (EggBitsetIter *iter, + const EggBitset *set, + guint *value); +gboolean egg_bitset_iter_init_at (EggBitsetIter *iter, + const EggBitset *set, + guint target, + guint *value); +gboolean egg_bitset_iter_next (EggBitsetIter *iter, + guint *value); +gboolean egg_bitset_iter_previous (EggBitsetIter *iter, + guint *value); +guint egg_bitset_iter_get_value (const EggBitsetIter *iter); +gboolean egg_bitset_iter_is_valid (const EggBitsetIter *iter); + +G_END_DECLS + diff --git a/contrib/eggbitset/meson.build b/contrib/eggbitset/meson.build new file mode 100644 index 00000000..0b5fd036 --- /dev/null +++ b/contrib/eggbitset/meson.build @@ -0,0 +1,17 @@ +libeggbitset_sources = [ + 'eggbitset.c', +] + +libeggbitset_deps = [ + dependency('gio-2.0', version: glib_req_version), +] + +libeggbitset_static = static_library('eggbitset', libeggbitset_sources, + gnu_symbol_visibility: 'hidden', + dependencies: libeggbitset_deps, +) + +libeggbitset_static_dep = declare_dependency( + include_directories: include_directories('.'), + link_with: libeggbitset_static, +) diff --git a/contrib/eggbitset/roaring.c b/contrib/eggbitset/roaring.c new file mode 100644 index 00000000..ff883844 --- /dev/null +++ b/contrib/eggbitset/roaring.c @@ -0,0 +1,11475 @@ +/* + * Amalgamated copy of CRoaring 0.2.66, modified for GTK to reduce compiler + * warnings. + * + * Copyright 2016-2020 The CRoaring authors + * Copyright 2020 Benjamin Otte + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeclaration-after-statement" + +#include "roaring.h" + +/* used for http://dmalloc.com/ Dmalloc - Debug Malloc Library */ +#ifdef DMALLOC +#include "dmalloc.h" +#endif + +/* begin file src/array_util.c */ +#include +#include +#include +#include +#include +#include + + +#ifdef USESSE4 +// used by intersect_vector16 +ALIGNED(0x1000) +static const uint8_t shuffle_mask16[] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 2, 3, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 4, 5, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 4, 5, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 4, 5, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 2, 3, 4, 5, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 6, 7, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 6, 7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 2, 3, 6, 7, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 2, 3, + 6, 7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 4, 5, 6, 7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 4, 5, 6, 7, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 2, 3, 4, 5, + 6, 7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 4, 5, 6, 7, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 8, 9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 8, 9, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 8, 9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 2, 3, 8, 9, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 4, 5, 8, 9, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 4, 5, 8, 9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 2, 3, 4, 5, 8, 9, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 2, 3, + 4, 5, 8, 9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 6, 7, 8, 9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 6, 7, 8, 9, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 2, 3, 6, 7, + 8, 9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 6, 7, 8, 9, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 4, 5, 6, 7, 8, 9, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 4, 5, + 6, 7, 8, 9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 4, 5, 6, 7, 8, 9, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 10, 11, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 10, 11, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 2, 3, 10, 11, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 2, 3, + 10, 11, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 4, 5, 10, 11, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 4, 5, 10, 11, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 2, 3, 4, 5, + 10, 11, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 4, 5, 10, 11, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 6, 7, 10, 11, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 6, 7, + 10, 11, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 6, 7, 10, 11, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 2, 3, 6, 7, 10, 11, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 4, 5, 6, 7, + 10, 11, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 4, 5, 6, 7, 10, 11, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 2, 3, 4, 5, 6, 7, 10, 11, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 2, 3, + 4, 5, 6, 7, 10, 11, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 8, 9, 10, 11, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 8, 9, 10, 11, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 2, 3, 8, 9, + 10, 11, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 8, 9, 10, 11, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 4, 5, 8, 9, 10, 11, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 4, 5, + 8, 9, 10, 11, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 4, 5, 8, 9, 10, 11, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 2, 3, 4, 5, 8, 9, + 10, 11, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 6, 7, 8, 9, + 10, 11, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 6, 7, 8, 9, 10, 11, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 2, 3, 6, 7, 8, 9, 10, 11, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 2, 3, + 6, 7, 8, 9, 10, 11, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 4, 5, 6, 7, 8, 9, 10, 11, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 4, 5, 6, 7, 8, 9, + 10, 11, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 2, 3, 4, 5, + 6, 7, 8, 9, 10, 11, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 0xFF, 0xFF, 0xFF, 0xFF, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 12, 13, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 2, 3, 12, 13, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 4, 5, 12, 13, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 4, 5, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 2, 3, 4, 5, 12, 13, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 2, 3, + 4, 5, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 6, 7, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 6, 7, 12, 13, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 2, 3, 6, 7, + 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 6, 7, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 4, 5, 6, 7, 12, 13, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 4, 5, + 6, 7, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 4, 5, 6, 7, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 2, 3, 4, 5, 6, 7, + 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 8, 9, 12, 13, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 8, 9, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 2, 3, 8, 9, 12, 13, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 2, 3, + 8, 9, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 4, 5, 8, 9, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 4, 5, 8, 9, 12, 13, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 2, 3, 4, 5, + 8, 9, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 4, 5, 8, 9, 12, 13, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 6, 7, 8, 9, 12, 13, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 6, 7, + 8, 9, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 6, 7, 8, 9, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 2, 3, 6, 7, 8, 9, + 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 4, 5, 6, 7, + 8, 9, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 4, 5, 6, 7, 8, 9, 12, 13, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 2, 3, 4, 5, 6, 7, 8, 9, + 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 2, 3, + 4, 5, 6, 7, 8, 9, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, + 10, 11, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 10, 11, 12, 13, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 2, 3, 10, 11, + 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 10, 11, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 4, 5, 10, 11, 12, 13, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 4, 5, + 10, 11, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 4, 5, 10, 11, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 2, 3, 4, 5, 10, 11, + 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 6, 7, 10, 11, + 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 6, 7, 10, 11, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 2, 3, 6, 7, 10, 11, 12, 13, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 2, 3, + 6, 7, 10, 11, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 4, 5, 6, 7, 10, 11, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 4, 5, 6, 7, 10, 11, + 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 2, 3, 4, 5, + 6, 7, 10, 11, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 4, 5, 6, 7, 10, 11, 12, 13, + 0xFF, 0xFF, 0xFF, 0xFF, 8, 9, 10, 11, 12, 13, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 8, 9, + 10, 11, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 8, 9, 10, 11, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 2, 3, 8, 9, 10, 11, + 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 4, 5, 8, 9, + 10, 11, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 4, 5, 8, 9, 10, 11, 12, 13, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 2, 3, 4, 5, 8, 9, 10, 11, + 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 2, 3, + 4, 5, 8, 9, 10, 11, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, + 6, 7, 8, 9, 10, 11, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 6, 7, 8, 9, 10, 11, + 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 2, 3, 6, 7, + 8, 9, 10, 11, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 6, 7, 8, 9, 10, 11, 12, 13, + 0xFF, 0xFF, 0xFF, 0xFF, 4, 5, 6, 7, 8, 9, 10, 11, + 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 4, 5, + 6, 7, 8, 9, 10, 11, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 0xFF, 0xFF, 14, 15, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 2, 3, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 2, 3, + 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 4, 5, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 4, 5, 14, 15, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 2, 3, 4, 5, + 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 4, 5, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 6, 7, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 6, 7, + 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 6, 7, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 2, 3, 6, 7, 14, 15, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 4, 5, 6, 7, + 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 4, 5, 6, 7, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 2, 3, 4, 5, 6, 7, 14, 15, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 2, 3, + 4, 5, 6, 7, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 8, 9, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 8, 9, 14, 15, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 2, 3, 8, 9, + 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 8, 9, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 4, 5, 8, 9, 14, 15, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 4, 5, + 8, 9, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 4, 5, 8, 9, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 2, 3, 4, 5, 8, 9, + 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 6, 7, 8, 9, + 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 6, 7, 8, 9, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 2, 3, 6, 7, 8, 9, 14, 15, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 2, 3, + 6, 7, 8, 9, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 4, 5, 6, 7, 8, 9, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 4, 5, 6, 7, 8, 9, + 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 2, 3, 4, 5, + 6, 7, 8, 9, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 14, 15, + 0xFF, 0xFF, 0xFF, 0xFF, 10, 11, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 10, 11, + 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 10, 11, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 2, 3, 10, 11, 14, 15, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 4, 5, 10, 11, + 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 4, 5, 10, 11, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 2, 3, 4, 5, 10, 11, 14, 15, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 2, 3, + 4, 5, 10, 11, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 6, 7, 10, 11, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 6, 7, 10, 11, 14, 15, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 2, 3, 6, 7, + 10, 11, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 6, 7, 10, 11, 14, 15, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 4, 5, 6, 7, 10, 11, 14, 15, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 4, 5, + 6, 7, 10, 11, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 4, 5, 6, 7, 10, 11, 14, 15, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 2, 3, 4, 5, 6, 7, + 10, 11, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 8, 9, 10, 11, + 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 8, 9, 10, 11, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 2, 3, 8, 9, 10, 11, 14, 15, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 2, 3, + 8, 9, 10, 11, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 4, 5, 8, 9, 10, 11, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 4, 5, 8, 9, 10, 11, + 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 2, 3, 4, 5, + 8, 9, 10, 11, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 4, 5, 8, 9, 10, 11, 14, 15, + 0xFF, 0xFF, 0xFF, 0xFF, 6, 7, 8, 9, 10, 11, 14, 15, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 6, 7, + 8, 9, 10, 11, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 6, 7, 8, 9, 10, 11, 14, 15, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 2, 3, 6, 7, 8, 9, + 10, 11, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 4, 5, 6, 7, + 8, 9, 10, 11, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 4, 5, 6, 7, 8, 9, 10, 11, 14, 15, + 0xFF, 0xFF, 0xFF, 0xFF, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 2, 3, + 4, 5, 6, 7, 8, 9, 10, 11, 14, 15, 0xFF, 0xFF, + 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 12, 13, 14, 15, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 2, 3, 12, 13, + 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 4, 5, 12, 13, 14, 15, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 4, 5, + 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 4, 5, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 2, 3, 4, 5, 12, 13, + 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 6, 7, 12, 13, + 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 6, 7, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 2, 3, 6, 7, 12, 13, 14, 15, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 2, 3, + 6, 7, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 4, 5, 6, 7, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 4, 5, 6, 7, 12, 13, + 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 2, 3, 4, 5, + 6, 7, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 4, 5, 6, 7, 12, 13, 14, 15, + 0xFF, 0xFF, 0xFF, 0xFF, 8, 9, 12, 13, 14, 15, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 8, 9, + 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 8, 9, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 2, 3, 8, 9, 12, 13, + 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 4, 5, 8, 9, + 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 4, 5, 8, 9, 12, 13, 14, 15, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 2, 3, 4, 5, 8, 9, 12, 13, + 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 2, 3, + 4, 5, 8, 9, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, + 6, 7, 8, 9, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 6, 7, 8, 9, 12, 13, + 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 2, 3, 6, 7, + 8, 9, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 6, 7, 8, 9, 12, 13, 14, 15, + 0xFF, 0xFF, 0xFF, 0xFF, 4, 5, 6, 7, 8, 9, 12, 13, + 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 4, 5, + 6, 7, 8, 9, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14, 15, + 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 12, 13, 14, 15, 0xFF, 0xFF, 10, 11, 12, 13, + 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 10, 11, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 2, 3, 10, 11, 12, 13, 14, 15, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 2, 3, + 10, 11, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 4, 5, 10, 11, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 4, 5, 10, 11, 12, 13, + 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 2, 3, 4, 5, + 10, 11, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 4, 5, 10, 11, 12, 13, 14, 15, + 0xFF, 0xFF, 0xFF, 0xFF, 6, 7, 10, 11, 12, 13, 14, 15, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 6, 7, + 10, 11, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 6, 7, 10, 11, 12, 13, 14, 15, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 2, 3, 6, 7, 10, 11, + 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 4, 5, 6, 7, + 10, 11, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 4, 5, 6, 7, 10, 11, 12, 13, 14, 15, + 0xFF, 0xFF, 0xFF, 0xFF, 2, 3, 4, 5, 6, 7, 10, 11, + 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 2, 3, + 4, 5, 6, 7, 10, 11, 12, 13, 14, 15, 0xFF, 0xFF, + 8, 9, 10, 11, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 8, 9, 10, 11, 12, 13, + 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 2, 3, 8, 9, + 10, 11, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 8, 9, 10, 11, 12, 13, 14, 15, + 0xFF, 0xFF, 0xFF, 0xFF, 4, 5, 8, 9, 10, 11, 12, 13, + 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 4, 5, + 8, 9, 10, 11, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 4, 5, 8, 9, 10, 11, 12, 13, 14, 15, + 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 2, 3, 4, 5, 8, 9, + 10, 11, 12, 13, 14, 15, 0xFF, 0xFF, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 0xFF, 0xFF, 0xFF, 0xFF, 2, 3, 6, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 2, 3, + 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0xFF, 0xFF, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 0xFF, 0xFF, 0xFF, 0xFF, 0, 1, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 0xFF, 0xFF, 2, 3, 4, 5, + 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0xFF, 0xFF, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 12, 13, 14, 15}; + +/** + * From Schlegel et al., Fast Sorted-Set Intersection using SIMD Instructions + * Optimized by D. Lemire on May 3rd 2013 + */ +int32_t intersect_vector16(const uint16_t *__restrict__ A, size_t s_a, + const uint16_t *__restrict__ B, size_t s_b, + uint16_t *C) { + size_t count = 0; + size_t i_a = 0, i_b = 0; + const int vectorlength = sizeof(__m128i) / sizeof(uint16_t); + const size_t st_a = (s_a / vectorlength) * vectorlength; + const size_t st_b = (s_b / vectorlength) * vectorlength; + __m128i v_a, v_b; + if ((i_a < st_a) && (i_b < st_b)) { + v_a = _mm_lddqu_si128((__m128i *)&A[i_a]); + v_b = _mm_lddqu_si128((__m128i *)&B[i_b]); + while ((A[i_a] == 0) || (B[i_b] == 0)) { + const __m128i res_v = _mm_cmpestrm( + v_b, vectorlength, v_a, vectorlength, + _SIDD_UWORD_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_BIT_MASK); + const int r = _mm_extract_epi32(res_v, 0); + __m128i sm16 = _mm_load_si128((const __m128i *)shuffle_mask16 + r); + __m128i p = _mm_shuffle_epi8(v_a, sm16); + _mm_storeu_si128((__m128i *)&C[count], p); // can overflow + count += _mm_popcnt_u32(r); + const uint16_t a_max = A[i_a + vectorlength - 1]; + const uint16_t b_max = B[i_b + vectorlength - 1]; + if (a_max <= b_max) { + i_a += vectorlength; + if (i_a == st_a) break; + v_a = _mm_lddqu_si128((__m128i *)&A[i_a]); + } + if (b_max <= a_max) { + i_b += vectorlength; + if (i_b == st_b) break; + v_b = _mm_lddqu_si128((__m128i *)&B[i_b]); + } + } + if ((i_a < st_a) && (i_b < st_b)) + while (true) { + const __m128i res_v = _mm_cmpistrm( + v_b, v_a, + _SIDD_UWORD_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_BIT_MASK); + const int r = _mm_extract_epi32(res_v, 0); + __m128i sm16 = + _mm_load_si128((const __m128i *)shuffle_mask16 + r); + __m128i p = _mm_shuffle_epi8(v_a, sm16); + _mm_storeu_si128((__m128i *)&C[count], p); // can overflow + count += _mm_popcnt_u32(r); + const uint16_t a_max = A[i_a + vectorlength - 1]; + const uint16_t b_max = B[i_b + vectorlength - 1]; + if (a_max <= b_max) { + i_a += vectorlength; + if (i_a == st_a) break; + v_a = _mm_lddqu_si128((__m128i *)&A[i_a]); + } + if (b_max <= a_max) { + i_b += vectorlength; + if (i_b == st_b) break; + v_b = _mm_lddqu_si128((__m128i *)&B[i_b]); + } + } + } + // intersect the tail using scalar intersection + while (i_a < s_a && i_b < s_b) { + uint16_t a = A[i_a]; + uint16_t b = B[i_b]; + if (a < b) { + i_a++; + } else if (b < a) { + i_b++; + } else { + C[count] = a; //==b; + count++; + i_a++; + i_b++; + } + } + return (int32_t)count; +} + +int32_t intersect_vector16_cardinality(const uint16_t *__restrict__ A, + size_t s_a, + const uint16_t *__restrict__ B, + size_t s_b) { + size_t count = 0; + size_t i_a = 0, i_b = 0; + const int vectorlength = sizeof(__m128i) / sizeof(uint16_t); + const size_t st_a = (s_a / vectorlength) * vectorlength; + const size_t st_b = (s_b / vectorlength) * vectorlength; + __m128i v_a, v_b; + if ((i_a < st_a) && (i_b < st_b)) { + v_a = _mm_lddqu_si128((__m128i *)&A[i_a]); + v_b = _mm_lddqu_si128((__m128i *)&B[i_b]); + while ((A[i_a] == 0) || (B[i_b] == 0)) { + const __m128i res_v = _mm_cmpestrm( + v_b, vectorlength, v_a, vectorlength, + _SIDD_UWORD_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_BIT_MASK); + const int r = _mm_extract_epi32(res_v, 0); + count += _mm_popcnt_u32(r); + const uint16_t a_max = A[i_a + vectorlength - 1]; + const uint16_t b_max = B[i_b + vectorlength - 1]; + if (a_max <= b_max) { + i_a += vectorlength; + if (i_a == st_a) break; + v_a = _mm_lddqu_si128((__m128i *)&A[i_a]); + } + if (b_max <= a_max) { + i_b += vectorlength; + if (i_b == st_b) break; + v_b = _mm_lddqu_si128((__m128i *)&B[i_b]); + } + } + if ((i_a < st_a) && (i_b < st_b)) + while (true) { + const __m128i res_v = _mm_cmpistrm( + v_b, v_a, + _SIDD_UWORD_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_BIT_MASK); + const int r = _mm_extract_epi32(res_v, 0); + count += _mm_popcnt_u32(r); + const uint16_t a_max = A[i_a + vectorlength - 1]; + const uint16_t b_max = B[i_b + vectorlength - 1]; + if (a_max <= b_max) { + i_a += vectorlength; + if (i_a == st_a) break; + v_a = _mm_lddqu_si128((__m128i *)&A[i_a]); + } + if (b_max <= a_max) { + i_b += vectorlength; + if (i_b == st_b) break; + v_b = _mm_lddqu_si128((__m128i *)&B[i_b]); + } + } + } + // intersect the tail using scalar intersection + while (i_a < s_a && i_b < s_b) { + uint16_t a = A[i_a]; + uint16_t b = B[i_b]; + if (a < b) { + i_a++; + } else if (b < a) { + i_b++; + } else { + count++; + i_a++; + i_b++; + } + } + return (int32_t)count; +} + +///////// +// Warning: +// This function may not be safe if A == C or B == C. +///////// +int32_t difference_vector16(const uint16_t *__restrict__ A, size_t s_a, + const uint16_t *__restrict__ B, size_t s_b, + uint16_t *C) { + // we handle the degenerate case + if (s_a == 0) return 0; + if (s_b == 0) { + if (A != C) memcpy(C, A, sizeof(uint16_t) * s_a); + return (int32_t)s_a; + } + // handle the leading zeroes, it is messy but it allows us to use the fast + // _mm_cmpistrm intrinsic safely + int32_t count = 0; + if ((A[0] == 0) || (B[0] == 0)) { + if ((A[0] == 0) && (B[0] == 0)) { + A++; + s_a--; + B++; + s_b--; + } else if (A[0] == 0) { + C[count++] = 0; + A++; + s_a--; + } else { + B++; + s_b--; + } + } + // at this point, we have two non-empty arrays, made of non-zero + // increasing values. + size_t i_a = 0, i_b = 0; + const size_t vectorlength = sizeof(__m128i) / sizeof(uint16_t); + const size_t st_a = (s_a / vectorlength) * vectorlength; + const size_t st_b = (s_b / vectorlength) * vectorlength; + if ((i_a < st_a) && (i_b < st_b)) { // this is the vectorized code path + __m128i v_a, v_b; //, v_bmax; + // we load a vector from A and a vector from B + v_a = _mm_lddqu_si128((__m128i *)&A[i_a]); + v_b = _mm_lddqu_si128((__m128i *)&B[i_b]); + // we have a runningmask which indicates which values from A have been + // spotted in B, these don't get written out. + __m128i runningmask_a_found_in_b = _mm_setzero_si128(); + /**** + * start of the main vectorized loop + *****/ + while (true) { + // afoundinb will contain a mask indicate for each entry in A + // whether it is seen + // in B + const __m128i a_found_in_b = + _mm_cmpistrm(v_b, v_a, _SIDD_UWORD_OPS | _SIDD_CMP_EQUAL_ANY | + _SIDD_BIT_MASK); + runningmask_a_found_in_b = + _mm_or_si128(runningmask_a_found_in_b, a_found_in_b); + // we always compare the last values of A and B + const uint16_t a_max = A[i_a + vectorlength - 1]; + const uint16_t b_max = B[i_b + vectorlength - 1]; + if (a_max <= b_max) { + // Ok. In this code path, we are ready to write our v_a + // because there is no need to read more from B, they will + // all be large values. + const int bitmask_belongs_to_difference = + _mm_extract_epi32(runningmask_a_found_in_b, 0) ^ 0xFF; + /*** next few lines are probably expensive *****/ + __m128i sm16 = _mm_load_si128((const __m128i *)shuffle_mask16 + + bitmask_belongs_to_difference); + __m128i p = _mm_shuffle_epi8(v_a, sm16); + _mm_storeu_si128((__m128i *)&C[count], p); // can overflow + count += _mm_popcnt_u32(bitmask_belongs_to_difference); + // we advance a + i_a += vectorlength; + if (i_a == st_a) // no more + break; + runningmask_a_found_in_b = _mm_setzero_si128(); + v_a = _mm_lddqu_si128((__m128i *)&A[i_a]); + } + if (b_max <= a_max) { + // in this code path, the current v_b has become useless + i_b += vectorlength; + if (i_b == st_b) break; + v_b = _mm_lddqu_si128((__m128i *)&B[i_b]); + } + } + // at this point, either we have i_a == st_a, which is the end of the + // vectorized processing, + // or we have i_b == st_b, and we are not done processing the vector... + // so we need to finish it off. + if (i_a < st_a) { // we have unfinished business... + uint16_t buffer[8]; // buffer to do a masked load + memset(buffer, 0, 8 * sizeof(uint16_t)); + memcpy(buffer, B + i_b, (s_b - i_b) * sizeof(uint16_t)); + v_b = _mm_lddqu_si128((__m128i *)buffer); + const __m128i a_found_in_b = + _mm_cmpistrm(v_b, v_a, _SIDD_UWORD_OPS | _SIDD_CMP_EQUAL_ANY | + _SIDD_BIT_MASK); + runningmask_a_found_in_b = + _mm_or_si128(runningmask_a_found_in_b, a_found_in_b); + const int bitmask_belongs_to_difference = + _mm_extract_epi32(runningmask_a_found_in_b, 0) ^ 0xFF; + __m128i sm16 = _mm_load_si128((const __m128i *)shuffle_mask16 + + bitmask_belongs_to_difference); + __m128i p = _mm_shuffle_epi8(v_a, sm16); + _mm_storeu_si128((__m128i *)&C[count], p); // can overflow + count += _mm_popcnt_u32(bitmask_belongs_to_difference); + i_a += vectorlength; + } + // at this point we should have i_a == st_a and i_b == st_b + } + // do the tail using scalar code + while (i_a < s_a && i_b < s_b) { + uint16_t a = A[i_a]; + uint16_t b = B[i_b]; + if (b < a) { + i_b++; + } else if (a < b) { + C[count] = a; + count++; + i_a++; + } else { //== + i_a++; + i_b++; + } + } + if (i_a < s_a) { + if(C == A) { + assert((size_t)count <= i_a); + if((size_t)count < i_a) { + memmove(C + count, A + i_a, sizeof(uint16_t) * (s_a - i_a)); + } + } else { + for(size_t i = 0; i < (s_a - i_a); i++) { + C[count + i] = A[i + i_a]; + } + } + count += (int32_t)(s_a - i_a); + } + return count; +} + +#endif // USESSE4 + + + +#ifdef USE_OLD_SKEW_INTERSECT +// TODO: given enough experience with the new skew intersect, drop the old one from the code base. + + +/* Computes the intersection between one small and one large set of uint16_t. + * Stores the result into buffer and return the number of elements. */ +int32_t intersect_skewed_uint16(const uint16_t *small, size_t size_s, + const uint16_t *large, size_t size_l, + uint16_t *buffer) { + size_t pos = 0, idx_l = 0, idx_s = 0; + + if (0 == size_s) { + return 0; + } + + uint16_t val_l = large[idx_l], val_s = small[idx_s]; + + while (true) { + if (val_l < val_s) { + idx_l = advanceUntil(large, (int32_t)idx_l, (int32_t)size_l, val_s); + if (idx_l == size_l) break; + val_l = large[idx_l]; + } else if (val_s < val_l) { + idx_s++; + if (idx_s == size_s) break; + val_s = small[idx_s]; + } else { + buffer[pos++] = val_s; + idx_s++; + if (idx_s == size_s) break; + val_s = small[idx_s]; + idx_l = advanceUntil(large, (int32_t)idx_l, (int32_t)size_l, val_s); + if (idx_l == size_l) break; + val_l = large[idx_l]; + } + } + + return (int32_t)pos; +} +#else // USE_OLD_SKEW_INTERSECT + + +/** +* Branchless binary search going after 4 values at once. +* Assumes that array is sorted. +* You have that array[*index1] >= target1, array[*index12] >= target2, ... +* except when *index1 = n, in which case you know that all values in array are +* smaller than target1, and so forth. +* It has logarithmic complexity. +*/ +static void binarySearch4(const uint16_t *array, int32_t n, uint16_t target1, + uint16_t target2, uint16_t target3, uint16_t target4, + int32_t *index1, int32_t *index2, int32_t *index3, + int32_t *index4) { + const uint16_t *base1 = array; + const uint16_t *base2 = array; + const uint16_t *base3 = array; + const uint16_t *base4 = array; + if (n == 0) + return; + while (n > 1) { + int32_t half = n >> 1; + base1 = (base1[half] < target1) ? &base1[half] : base1; + base2 = (base2[half] < target2) ? &base2[half] : base2; + base3 = (base3[half] < target3) ? &base3[half] : base3; + base4 = (base4[half] < target4) ? &base4[half] : base4; + n -= half; + } + *index1 = (int32_t)((*base1 < target1) + base1 - array); + *index2 = (int32_t)((*base2 < target2) + base2 - array); + *index3 = (int32_t)((*base3 < target3) + base3 - array); + *index4 = (int32_t)((*base4 < target4) + base4 - array); +} + +/** +* Branchless binary search going after 2 values at once. +* Assumes that array is sorted. +* You have that array[*index1] >= target1, array[*index12] >= target2. +* except when *index1 = n, in which case you know that all values in array are +* smaller than target1, and so forth. +* It has logarithmic complexity. +*/ +static void binarySearch2(const uint16_t *array, int32_t n, uint16_t target1, + uint16_t target2, int32_t *index1, int32_t *index2) { + const uint16_t *base1 = array; + const uint16_t *base2 = array; + if (n == 0) + return; + while (n > 1) { + int32_t half = n >> 1; + base1 = (base1[half] < target1) ? &base1[half] : base1; + base2 = (base2[half] < target2) ? &base2[half] : base2; + n -= half; + } + *index1 = (int32_t)((*base1 < target1) + base1 - array); + *index2 = (int32_t)((*base2 < target2) + base2 - array); +} + +/* Computes the intersection between one small and one large set of uint16_t. + * Stores the result into buffer and return the number of elements. + * Processes the small set in blocks of 4 values calling binarySearch4 + * and binarySearch2. This approach can be slightly superior to a conventional + * galloping search in some instances. + */ +int32_t intersect_skewed_uint16(const uint16_t *small, size_t size_s, + const uint16_t *large, size_t size_l, + uint16_t *buffer) { + size_t pos = 0, idx_l = 0, idx_s = 0; + + if (0 == size_s) { + return 0; + } + int32_t index1 = 0, index2 = 0, index3 = 0, index4 = 0; + while ((idx_s + 4 <= size_s) && (idx_l < size_l)) { + uint16_t target1 = small[idx_s]; + uint16_t target2 = small[idx_s + 1]; + uint16_t target3 = small[idx_s + 2]; + uint16_t target4 = small[idx_s + 3]; + binarySearch4(large + idx_l, (int32_t)(size_l - idx_l), target1, target2, target3, + target4, &index1, &index2, &index3, &index4); + if ((index1 + idx_l < size_l) && (large[idx_l + index1] == target1)) { + buffer[pos++] = target1; + } + if ((index2 + idx_l < size_l) && (large[idx_l + index2] == target2)) { + buffer[pos++] = target2; + } + if ((index3 + idx_l < size_l) && (large[idx_l + index3] == target3)) { + buffer[pos++] = target3; + } + if ((index4 + idx_l < size_l) && (large[idx_l + index4] == target4)) { + buffer[pos++] = target4; + } + idx_s += 4; + idx_l += index4; + } + if ((idx_s + 2 <= size_s) && (idx_l < size_l)) { + uint16_t target1 = small[idx_s]; + uint16_t target2 = small[idx_s + 1]; + binarySearch2(large + idx_l, (int32_t)(size_l - idx_l), target1, target2, &index1, + &index2); + if ((index1 + idx_l < size_l) && (large[idx_l + index1] == target1)) { + buffer[pos++] = target1; + } + if ((index2 + idx_l < size_l) && (large[idx_l + index2] == target2)) { + buffer[pos++] = target2; + } + idx_s += 2; + idx_l += index2; + } + if ((idx_s < size_s) && (idx_l < size_l)) { + uint16_t val_s = small[idx_s]; + int32_t index = binarySearch(large + idx_l, (int32_t)(size_l - idx_l), val_s); + if (index >= 0) + buffer[pos++] = val_s; + } + return (int32_t)pos; +} + + +#endif //USE_OLD_SKEW_INTERSECT + + +// TODO: this could be accelerated, possibly, by using binarySearch4 as above. +int32_t intersect_skewed_uint16_cardinality(const uint16_t *small, + size_t size_s, + const uint16_t *large, + size_t size_l) { + size_t pos = 0, idx_l = 0, idx_s = 0; + + if (0 == size_s) { + return 0; + } + + uint16_t val_l = large[idx_l], val_s = small[idx_s]; + + while (true) { + if (val_l < val_s) { + idx_l = advanceUntil(large, (int32_t)idx_l, (int32_t)size_l, val_s); + if (idx_l == size_l) break; + val_l = large[idx_l]; + } else if (val_s < val_l) { + idx_s++; + if (idx_s == size_s) break; + val_s = small[idx_s]; + } else { + pos++; + idx_s++; + if (idx_s == size_s) break; + val_s = small[idx_s]; + idx_l = advanceUntil(large, (int32_t)idx_l, (int32_t)size_l, val_s); + if (idx_l == size_l) break; + val_l = large[idx_l]; + } + } + + return (int32_t)pos; +} + +bool intersect_skewed_uint16_nonempty(const uint16_t *small, size_t size_s, + const uint16_t *large, size_t size_l) { + size_t idx_l = 0, idx_s = 0; + + if (0 == size_s) { + return false; + } + + uint16_t val_l = large[idx_l], val_s = small[idx_s]; + + while (true) { + if (val_l < val_s) { + idx_l = advanceUntil(large, (int32_t)idx_l, (int32_t)size_l, val_s); + if (idx_l == size_l) break; + val_l = large[idx_l]; + } else if (val_s < val_l) { + idx_s++; + if (idx_s == size_s) break; + val_s = small[idx_s]; + } else { + return true; + } + } + + return false; +} + +/** + * Generic intersection function. + */ +int32_t intersect_uint16(const uint16_t *A, const size_t lenA, + const uint16_t *B, const size_t lenB, uint16_t *out) { + const uint16_t *initout = out; + if (lenA == 0 || lenB == 0) return 0; + const uint16_t *endA = A + lenA; + const uint16_t *endB = B + lenB; + + while (1) { + while (*A < *B) { + SKIP_FIRST_COMPARE: + if (++A == endA) return (int32_t)(out - initout); + } + while (*A > *B) { + if (++B == endB) return (int32_t)(out - initout); + } + if (*A == *B) { + *out++ = *A; + if (++A == endA || ++B == endB) return (int32_t)(out - initout); + } else { + goto SKIP_FIRST_COMPARE; + } + } + return (int32_t)(out - initout); // NOTREACHED +} + +int32_t intersect_uint16_cardinality(const uint16_t *A, const size_t lenA, + const uint16_t *B, const size_t lenB) { + int32_t answer = 0; + if (lenA == 0 || lenB == 0) return 0; + const uint16_t *endA = A + lenA; + const uint16_t *endB = B + lenB; + + while (1) { + while (*A < *B) { + SKIP_FIRST_COMPARE: + if (++A == endA) return answer; + } + while (*A > *B) { + if (++B == endB) return answer; + } + if (*A == *B) { + ++answer; + if (++A == endA || ++B == endB) return answer; + } else { + goto SKIP_FIRST_COMPARE; + } + } + return answer; // NOTREACHED +} + + +bool intersect_uint16_nonempty(const uint16_t *A, const size_t lenA, + const uint16_t *B, const size_t lenB) { + if (lenA == 0 || lenB == 0) return 0; + const uint16_t *endA = A + lenA; + const uint16_t *endB = B + lenB; + + while (1) { + while (*A < *B) { + SKIP_FIRST_COMPARE: + if (++A == endA) return false; + } + while (*A > *B) { + if (++B == endB) return false; + } + if (*A == *B) { + return true; + } else { + goto SKIP_FIRST_COMPARE; + } + } + return false; // NOTREACHED +} + + + +/** + * Generic intersection function. + */ +size_t intersection_uint32(const uint32_t *A, const size_t lenA, + const uint32_t *B, const size_t lenB, + uint32_t *out) { + const uint32_t *initout = out; + if (lenA == 0 || lenB == 0) return 0; + const uint32_t *endA = A + lenA; + const uint32_t *endB = B + lenB; + + while (1) { + while (*A < *B) { + SKIP_FIRST_COMPARE: + if (++A == endA) return (out - initout); + } + while (*A > *B) { + if (++B == endB) return (out - initout); + } + if (*A == *B) { + *out++ = *A; + if (++A == endA || ++B == endB) return (out - initout); + } else { + goto SKIP_FIRST_COMPARE; + } + } + return (out - initout); // NOTREACHED +} + +size_t intersection_uint32_card(const uint32_t *A, const size_t lenA, + const uint32_t *B, const size_t lenB) { + if (lenA == 0 || lenB == 0) return 0; + size_t card = 0; + const uint32_t *endA = A + lenA; + const uint32_t *endB = B + lenB; + + while (1) { + while (*A < *B) { + SKIP_FIRST_COMPARE: + if (++A == endA) return card; + } + while (*A > *B) { + if (++B == endB) return card; + } + if (*A == *B) { + card++; + if (++A == endA || ++B == endB) return card; + } else { + goto SKIP_FIRST_COMPARE; + } + } + return card; // NOTREACHED +} + +// can one vectorize the computation of the union? (Update: Yes! See +// union_vector16). + +size_t union_uint16(const uint16_t *set_1, size_t size_1, const uint16_t *set_2, + size_t size_2, uint16_t *buffer) { + size_t pos = 0, idx_1 = 0, idx_2 = 0; + + if (0 == size_2) { + memmove(buffer, set_1, size_1 * sizeof(uint16_t)); + return size_1; + } + if (0 == size_1) { + memmove(buffer, set_2, size_2 * sizeof(uint16_t)); + return size_2; + } + + uint16_t val_1 = set_1[idx_1], val_2 = set_2[idx_2]; + + while (true) { + if (val_1 < val_2) { + buffer[pos++] = val_1; + ++idx_1; + if (idx_1 >= size_1) break; + val_1 = set_1[idx_1]; + } else if (val_2 < val_1) { + buffer[pos++] = val_2; + ++idx_2; + if (idx_2 >= size_2) break; + val_2 = set_2[idx_2]; + } else { + buffer[pos++] = val_1; + ++idx_1; + ++idx_2; + if (idx_1 >= size_1 || idx_2 >= size_2) break; + val_1 = set_1[idx_1]; + val_2 = set_2[idx_2]; + } + } + + if (idx_1 < size_1) { + const size_t n_elems = size_1 - idx_1; + memmove(buffer + pos, set_1 + idx_1, n_elems * sizeof(uint16_t)); + pos += n_elems; + } else if (idx_2 < size_2) { + const size_t n_elems = size_2 - idx_2; + memmove(buffer + pos, set_2 + idx_2, n_elems * sizeof(uint16_t)); + pos += n_elems; + } + + return pos; +} + +int difference_uint16(const uint16_t *a1, int length1, const uint16_t *a2, + int length2, uint16_t *a_out) { + int out_card = 0; + int k1 = 0, k2 = 0; + if (length1 == 0) return 0; + if (length2 == 0) { + if (a1 != a_out) memcpy(a_out, a1, sizeof(uint16_t) * length1); + return length1; + } + uint16_t s1 = a1[k1]; + uint16_t s2 = a2[k2]; + while (true) { + if (s1 < s2) { + a_out[out_card++] = s1; + ++k1; + if (k1 >= length1) { + break; + } + s1 = a1[k1]; + } else if (s1 == s2) { + ++k1; + ++k2; + if (k1 >= length1) { + break; + } + if (k2 >= length2) { + memmove(a_out + out_card, a1 + k1, + sizeof(uint16_t) * (length1 - k1)); + return out_card + length1 - k1; + } + s1 = a1[k1]; + s2 = a2[k2]; + } else { // if (val1>val2) + ++k2; + if (k2 >= length2) { + memmove(a_out + out_card, a1 + k1, + sizeof(uint16_t) * (length1 - k1)); + return out_card + length1 - k1; + } + s2 = a2[k2]; + } + } + return out_card; +} + +int32_t xor_uint16(const uint16_t *array_1, int32_t card_1, + const uint16_t *array_2, int32_t card_2, uint16_t *out) { + int32_t pos1 = 0, pos2 = 0, pos_out = 0; + while (pos1 < card_1 && pos2 < card_2) { + const uint16_t v1 = array_1[pos1]; + const uint16_t v2 = array_2[pos2]; + if (v1 == v2) { + ++pos1; + ++pos2; + continue; + } + if (v1 < v2) { + out[pos_out++] = v1; + ++pos1; + } else { + out[pos_out++] = v2; + ++pos2; + } + } + if (pos1 < card_1) { + const size_t n_elems = card_1 - pos1; + memcpy(out + pos_out, array_1 + pos1, n_elems * sizeof(uint16_t)); + pos_out += (int32_t)n_elems; + } else if (pos2 < card_2) { + const size_t n_elems = card_2 - pos2; + memcpy(out + pos_out, array_2 + pos2, n_elems * sizeof(uint16_t)); + pos_out += (int32_t)n_elems; + } + return pos_out; +} + +#ifdef USESSE4 + +/*** + * start of the SIMD 16-bit union code + * + */ + +// Assuming that vInput1 and vInput2 are sorted, produces a sorted output going +// from vecMin all the way to vecMax +// developed originally for merge sort using SIMD instructions. +// Standard merge. See, e.g., Inoue and Taura, SIMD- and Cache-Friendly +// Algorithm for Sorting an Array of Structures +static inline void sse_merge(const __m128i *vInput1, + const __m128i *vInput2, // input 1 & 2 + __m128i *vecMin, __m128i *vecMax) { // output + __m128i vecTmp; + vecTmp = _mm_min_epu16(*vInput1, *vInput2); + *vecMax = _mm_max_epu16(*vInput1, *vInput2); + vecTmp = _mm_alignr_epi8(vecTmp, vecTmp, 2); + *vecMin = _mm_min_epu16(vecTmp, *vecMax); + *vecMax = _mm_max_epu16(vecTmp, *vecMax); + vecTmp = _mm_alignr_epi8(*vecMin, *vecMin, 2); + *vecMin = _mm_min_epu16(vecTmp, *vecMax); + *vecMax = _mm_max_epu16(vecTmp, *vecMax); + vecTmp = _mm_alignr_epi8(*vecMin, *vecMin, 2); + *vecMin = _mm_min_epu16(vecTmp, *vecMax); + *vecMax = _mm_max_epu16(vecTmp, *vecMax); + vecTmp = _mm_alignr_epi8(*vecMin, *vecMin, 2); + *vecMin = _mm_min_epu16(vecTmp, *vecMax); + *vecMax = _mm_max_epu16(vecTmp, *vecMax); + vecTmp = _mm_alignr_epi8(*vecMin, *vecMin, 2); + *vecMin = _mm_min_epu16(vecTmp, *vecMax); + *vecMax = _mm_max_epu16(vecTmp, *vecMax); + vecTmp = _mm_alignr_epi8(*vecMin, *vecMin, 2); + *vecMin = _mm_min_epu16(vecTmp, *vecMax); + *vecMax = _mm_max_epu16(vecTmp, *vecMax); + vecTmp = _mm_alignr_epi8(*vecMin, *vecMin, 2); + *vecMin = _mm_min_epu16(vecTmp, *vecMax); + *vecMax = _mm_max_epu16(vecTmp, *vecMax); + *vecMin = _mm_alignr_epi8(*vecMin, *vecMin, 2); +} + +// used by store_unique, generated by simdunion.py +static uint8_t uniqshuf[] = { + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, + 0xc, 0xd, 0xe, 0xf, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, + 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0x0, 0x1, 0x4, 0x5, + 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, + 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, + 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x2, 0x3, 0x6, 0x7, 0x8, 0x9, + 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0x2, 0x3, 0x6, 0x7, + 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, + 0xFF, 0xFF, 0xFF, 0xFF, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, + 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x2, 0x3, + 0x4, 0x5, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, + 0x2, 0x3, 0x4, 0x5, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, + 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x4, 0x5, 0x8, 0x9, 0xa, 0xb, + 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0x4, 0x5, 0x8, 0x9, + 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, + 0xFF, 0xFF, 0xFF, 0xFF, 0x2, 0x3, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, + 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x8, 0x9, + 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0x2, 0x3, 0x4, 0x5, + 0x6, 0x7, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x4, 0x5, 0x6, 0x7, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, + 0xFF, 0xFF, 0xFF, 0xFF, 0x4, 0x5, 0x6, 0x7, 0xa, 0xb, 0xc, 0xd, + 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x2, 0x3, + 0x6, 0x7, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x6, 0x7, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x6, 0x7, 0xa, 0xb, 0xc, 0xd, + 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x6, 0x7, 0xa, 0xb, + 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, + 0xFF, 0xFF, 0xFF, 0xFF, 0x2, 0x3, 0x4, 0x5, 0xa, 0xb, 0xc, 0xd, + 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x4, 0x5, + 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x4, 0x5, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x2, 0x3, 0xa, 0xb, 0xc, 0xd, + 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x2, 0x3, 0xa, 0xb, + 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x2, 0x3, + 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, + 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xc, 0xd, 0xe, 0xf, + 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, + 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0x4, 0x5, 0x6, 0x7, + 0x8, 0x9, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x6, 0x7, 0x8, 0x9, 0xc, 0xd, 0xe, 0xf, + 0xFF, 0xFF, 0xFF, 0xFF, 0x2, 0x3, 0x6, 0x7, 0x8, 0x9, 0xc, 0xd, + 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x6, 0x7, + 0x8, 0x9, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x6, 0x7, 0x8, 0x9, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x8, 0x9, + 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0x2, 0x3, 0x4, 0x5, + 0x8, 0x9, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x4, 0x5, 0x8, 0x9, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x4, 0x5, 0x8, 0x9, 0xc, 0xd, 0xe, 0xf, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x2, 0x3, + 0x8, 0x9, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x8, 0x9, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x8, 0x9, 0xc, 0xd, 0xe, 0xf, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x8, 0x9, 0xc, 0xd, + 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0xc, 0xd, 0xe, 0xf, + 0xFF, 0xFF, 0xFF, 0xFF, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0xc, 0xd, + 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x4, 0x5, + 0x6, 0x7, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x4, 0x5, 0x6, 0x7, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x2, 0x3, 0x6, 0x7, 0xc, 0xd, + 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x2, 0x3, 0x6, 0x7, + 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x6, 0x7, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x6, 0x7, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x2, 0x3, + 0x4, 0x5, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x4, 0x5, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x4, 0x5, 0xc, 0xd, 0xe, 0xf, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x4, 0x5, 0xc, 0xd, + 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x2, 0x3, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0xc, 0xd, + 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x8, 0x9, 0xa, 0xb, 0xe, 0xf, 0xFF, 0xFF, 0x2, 0x3, 0x4, 0x5, + 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xe, 0xf, + 0xFF, 0xFF, 0xFF, 0xFF, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, + 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x2, 0x3, + 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xe, 0xf, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, + 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x6, 0x7, 0x8, 0x9, + 0xa, 0xb, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x8, 0x9, 0xa, 0xb, 0xe, 0xf, + 0xFF, 0xFF, 0xFF, 0xFF, 0x2, 0x3, 0x4, 0x5, 0x8, 0x9, 0xa, 0xb, + 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x4, 0x5, + 0x8, 0x9, 0xa, 0xb, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x4, 0x5, 0x8, 0x9, 0xa, 0xb, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x2, 0x3, 0x8, 0x9, 0xa, 0xb, + 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x2, 0x3, 0x8, 0x9, + 0xa, 0xb, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x8, 0x9, 0xa, 0xb, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x8, 0x9, 0xa, 0xb, 0xe, 0xf, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x2, 0x3, + 0x4, 0x5, 0x6, 0x7, 0xa, 0xb, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0xa, 0xb, 0xe, 0xf, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x4, 0x5, 0x6, 0x7, 0xa, 0xb, + 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x4, 0x5, 0x6, 0x7, + 0xa, 0xb, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x6, 0x7, 0xa, 0xb, 0xe, 0xf, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x2, 0x3, 0x6, 0x7, 0xa, 0xb, 0xe, 0xf, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x6, 0x7, + 0xa, 0xb, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x6, 0x7, 0xa, 0xb, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0xa, 0xb, + 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x2, 0x3, 0x4, 0x5, + 0xa, 0xb, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x4, 0x5, 0xa, 0xb, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x4, 0x5, 0xa, 0xb, 0xe, 0xf, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x2, 0x3, + 0xa, 0xb, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0xa, 0xb, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0xa, 0xb, 0xe, 0xf, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xa, 0xb, 0xe, 0xf, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xe, 0xf, + 0xFF, 0xFF, 0xFF, 0xFF, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, + 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x4, 0x5, + 0x6, 0x7, 0x8, 0x9, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x2, 0x3, 0x6, 0x7, 0x8, 0x9, + 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x2, 0x3, 0x6, 0x7, + 0x8, 0x9, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x6, 0x7, 0x8, 0x9, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x6, 0x7, 0x8, 0x9, 0xe, 0xf, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x2, 0x3, + 0x4, 0x5, 0x8, 0x9, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x4, 0x5, 0x8, 0x9, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x4, 0x5, 0x8, 0x9, 0xe, 0xf, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x4, 0x5, 0x8, 0x9, + 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x8, 0x9, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x2, 0x3, 0x8, 0x9, 0xe, 0xf, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x8, 0x9, + 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x8, 0x9, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x2, 0x3, 0x4, 0x5, + 0x6, 0x7, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x4, 0x5, 0x6, 0x7, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x4, 0x5, 0x6, 0x7, 0xe, 0xf, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x2, 0x3, + 0x6, 0x7, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x6, 0x7, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x6, 0x7, 0xe, 0xf, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x6, 0x7, 0xe, 0xf, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x2, 0x3, 0x4, 0x5, 0xe, 0xf, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x4, 0x5, + 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x4, 0x5, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x2, 0x3, 0xe, 0xf, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x2, 0x3, 0xe, 0xf, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x2, 0x3, + 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xFF, 0xFF, + 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, + 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, + 0xa, 0xb, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0x4, 0x5, 0x6, 0x7, + 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, + 0xFF, 0xFF, 0xFF, 0xFF, 0x2, 0x3, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, + 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x6, 0x7, + 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x8, 0x9, + 0xa, 0xb, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0x2, 0x3, 0x4, 0x5, + 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x4, 0x5, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x4, 0x5, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x2, 0x3, + 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x8, 0x9, 0xa, 0xb, + 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0xa, 0xb, 0xc, 0xd, + 0xFF, 0xFF, 0xFF, 0xFF, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0xa, 0xb, + 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x4, 0x5, + 0x6, 0x7, 0xa, 0xb, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x4, 0x5, 0x6, 0x7, 0xa, 0xb, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x2, 0x3, 0x6, 0x7, 0xa, 0xb, + 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x2, 0x3, 0x6, 0x7, + 0xa, 0xb, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x6, 0x7, 0xa, 0xb, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x6, 0x7, 0xa, 0xb, 0xc, 0xd, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x2, 0x3, + 0x4, 0x5, 0xa, 0xb, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x4, 0x5, 0xa, 0xb, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x4, 0x5, 0xa, 0xb, 0xc, 0xd, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x4, 0x5, 0xa, 0xb, + 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0xa, 0xb, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x2, 0x3, 0xa, 0xb, 0xc, 0xd, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0xa, 0xb, + 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xa, 0xb, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x8, 0x9, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0x2, 0x3, 0x4, 0x5, + 0x6, 0x7, 0x8, 0x9, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xc, 0xd, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xc, 0xd, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x2, 0x3, + 0x6, 0x7, 0x8, 0x9, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x6, 0x7, 0x8, 0x9, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x6, 0x7, 0x8, 0x9, 0xc, 0xd, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x6, 0x7, 0x8, 0x9, + 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x8, 0x9, 0xc, 0xd, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x2, 0x3, 0x4, 0x5, 0x8, 0x9, 0xc, 0xd, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x4, 0x5, + 0x8, 0x9, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x4, 0x5, 0x8, 0x9, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x2, 0x3, 0x8, 0x9, 0xc, 0xd, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x2, 0x3, 0x8, 0x9, + 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x8, 0x9, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x8, 0x9, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x2, 0x3, + 0x4, 0x5, 0x6, 0x7, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x4, 0x5, 0x6, 0x7, 0xc, 0xd, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x4, 0x5, 0x6, 0x7, + 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x6, 0x7, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x2, 0x3, 0x6, 0x7, 0xc, 0xd, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x6, 0x7, + 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x6, 0x7, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0xc, 0xd, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x2, 0x3, 0x4, 0x5, + 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x4, 0x5, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x4, 0x5, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x2, 0x3, + 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xc, 0xd, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, + 0xFF, 0xFF, 0xFF, 0xFF, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, + 0xa, 0xb, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x4, 0x5, + 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x2, 0x3, 0x6, 0x7, 0x8, 0x9, + 0xa, 0xb, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x2, 0x3, 0x6, 0x7, + 0x8, 0x9, 0xa, 0xb, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x2, 0x3, + 0x4, 0x5, 0x8, 0x9, 0xa, 0xb, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x4, 0x5, 0x8, 0x9, 0xa, 0xb, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x4, 0x5, 0x8, 0x9, 0xa, 0xb, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x4, 0x5, 0x8, 0x9, + 0xa, 0xb, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x8, 0x9, 0xa, 0xb, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x2, 0x3, 0x8, 0x9, 0xa, 0xb, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x8, 0x9, + 0xa, 0xb, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x8, 0x9, 0xa, 0xb, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0xa, 0xb, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x2, 0x3, 0x4, 0x5, + 0x6, 0x7, 0xa, 0xb, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x4, 0x5, 0x6, 0x7, 0xa, 0xb, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x4, 0x5, 0x6, 0x7, 0xa, 0xb, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x2, 0x3, + 0x6, 0x7, 0xa, 0xb, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x6, 0x7, 0xa, 0xb, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x6, 0x7, 0xa, 0xb, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x6, 0x7, 0xa, 0xb, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0xa, 0xb, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x2, 0x3, 0x4, 0x5, 0xa, 0xb, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x4, 0x5, + 0xa, 0xb, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x4, 0x5, 0xa, 0xb, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x2, 0x3, 0xa, 0xb, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x2, 0x3, 0xa, 0xb, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0xa, 0xb, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xa, 0xb, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x2, 0x3, + 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x4, 0x5, 0x6, 0x7, + 0x8, 0x9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x6, 0x7, 0x8, 0x9, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x2, 0x3, 0x6, 0x7, 0x8, 0x9, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x6, 0x7, + 0x8, 0x9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x6, 0x7, 0x8, 0x9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x8, 0x9, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x2, 0x3, 0x4, 0x5, + 0x8, 0x9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x4, 0x5, 0x8, 0x9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x4, 0x5, 0x8, 0x9, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x2, 0x3, + 0x8, 0x9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x8, 0x9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x8, 0x9, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x8, 0x9, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x4, 0x5, + 0x6, 0x7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x4, 0x5, 0x6, 0x7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x2, 0x3, 0x6, 0x7, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x2, 0x3, 0x6, 0x7, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x6, 0x7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x6, 0x7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x2, 0x3, + 0x4, 0x5, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x4, 0x5, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0x4, 0x5, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x4, 0x5, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x2, 0x3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x1, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF}; + +// write vector new, while omitting repeated values assuming that previously +// written vector was "old" +static inline int store_unique(__m128i old, __m128i newval, uint16_t *output) { + __m128i vecTmp = _mm_alignr_epi8(newval, old, 16 - 2); + // lots of high latency instructions follow (optimize?) + int M = _mm_movemask_epi8( + _mm_packs_epi16(_mm_cmpeq_epi16(vecTmp, newval), _mm_setzero_si128())); + int numberofnewvalues = 8 - _mm_popcnt_u32(M); + __m128i key = _mm_lddqu_si128((const __m128i *)uniqshuf + M); + __m128i val = _mm_shuffle_epi8(newval, key); + _mm_storeu_si128((__m128i *)output, val); + return numberofnewvalues; +} + +// working in-place, this function overwrites the repeated values +// could be avoided? +static inline uint32_t unique(uint16_t *out, uint32_t len) { + uint32_t pos = 1; + for (uint32_t i = 1; i < len; ++i) { + if (out[i] != out[i - 1]) { + out[pos++] = out[i]; + } + } + return pos; +} + +// use with qsort, could be avoided +static int uint16_compare(const void *a, const void *b) { + return (*(uint16_t *)a - *(uint16_t *)b); +} + +// a one-pass SSE union algorithm +// This function may not be safe if array1 == output or array2 == output. +uint32_t union_vector16(const uint16_t *__restrict__ array1, uint32_t length1, + const uint16_t *__restrict__ array2, uint32_t length2, + uint16_t *__restrict__ output) { + if ((length1 < 8) || (length2 < 8)) { + return (uint32_t)union_uint16(array1, length1, array2, length2, output); + } + __m128i vA, vB, V, vecMin, vecMax; + __m128i laststore; + uint16_t *initoutput = output; + uint32_t len1 = length1 / 8; + uint32_t len2 = length2 / 8; + uint32_t pos1 = 0; + uint32_t pos2 = 0; + // we start the machine + vA = _mm_lddqu_si128((const __m128i *)array1 + pos1); + pos1++; + vB = _mm_lddqu_si128((const __m128i *)array2 + pos2); + pos2++; + sse_merge(&vA, &vB, &vecMin, &vecMax); + laststore = _mm_set1_epi16(-1); + output += store_unique(laststore, vecMin, output); + laststore = vecMin; + if ((pos1 < len1) && (pos2 < len2)) { + uint16_t curA, curB; + curA = array1[8 * pos1]; + curB = array2[8 * pos2]; + while (true) { + if (curA <= curB) { + V = _mm_lddqu_si128((const __m128i *)array1 + pos1); + pos1++; + if (pos1 < len1) { + curA = array1[8 * pos1]; + } else { + break; + } + } else { + V = _mm_lddqu_si128((const __m128i *)array2 + pos2); + pos2++; + if (pos2 < len2) { + curB = array2[8 * pos2]; + } else { + break; + } + } + sse_merge(&V, &vecMax, &vecMin, &vecMax); + output += store_unique(laststore, vecMin, output); + laststore = vecMin; + } + sse_merge(&V, &vecMax, &vecMin, &vecMax); + output += store_unique(laststore, vecMin, output); + laststore = vecMin; + } + // we finish the rest off using a scalar algorithm + // could be improved? + // + // copy the small end on a tmp buffer + uint32_t len = (uint32_t)(output - initoutput); + uint16_t buffer[16]; + uint32_t leftoversize = store_unique(laststore, vecMax, buffer); + if (pos1 == len1) { + memcpy(buffer + leftoversize, array1 + 8 * pos1, + (length1 - 8 * len1) * sizeof(uint16_t)); + leftoversize += length1 - 8 * len1; + qsort(buffer, leftoversize, sizeof(uint16_t), uint16_compare); + + leftoversize = unique(buffer, leftoversize); + len += (uint32_t)union_uint16(buffer, leftoversize, array2 + 8 * pos2, + length2 - 8 * pos2, output); + } else { + memcpy(buffer + leftoversize, array2 + 8 * pos2, + (length2 - 8 * len2) * sizeof(uint16_t)); + leftoversize += length2 - 8 * len2; + qsort(buffer, leftoversize, sizeof(uint16_t), uint16_compare); + leftoversize = unique(buffer, leftoversize); + len += (uint32_t)union_uint16(buffer, leftoversize, array1 + 8 * pos1, + length1 - 8 * pos1, output); + } + return len; +} + +/** + * End of the SIMD 16-bit union code + * + */ + +/** + * Start of SIMD 16-bit XOR code + */ + +// write vector new, while omitting repeated values assuming that previously +// written vector was "old" +static inline int store_unique_xor(__m128i old, __m128i newval, + uint16_t *output) { + __m128i vecTmp1 = _mm_alignr_epi8(newval, old, 16 - 4); + __m128i vecTmp2 = _mm_alignr_epi8(newval, old, 16 - 2); + __m128i equalleft = _mm_cmpeq_epi16(vecTmp2, vecTmp1); + __m128i equalright = _mm_cmpeq_epi16(vecTmp2, newval); + __m128i equalleftoright = _mm_or_si128(equalleft, equalright); + int M = _mm_movemask_epi8( + _mm_packs_epi16(equalleftoright, _mm_setzero_si128())); + int numberofnewvalues = 8 - _mm_popcnt_u32(M); + __m128i key = _mm_lddqu_si128((const __m128i *)uniqshuf + M); + __m128i val = _mm_shuffle_epi8(vecTmp2, key); + _mm_storeu_si128((__m128i *)output, val); + return numberofnewvalues; +} + +// working in-place, this function overwrites the repeated values +// could be avoided? Warning: assumes len > 0 +static inline uint32_t unique_xor(uint16_t *out, uint32_t len) { + uint32_t pos = 1; + for (uint32_t i = 1; i < len; ++i) { + if (out[i] != out[i - 1]) { + out[pos++] = out[i]; + } else + pos--; // if it is identical to previous, delete it + } + return pos; +} + +// a one-pass SSE xor algorithm +uint32_t xor_vector16(const uint16_t *__restrict__ array1, uint32_t length1, + const uint16_t *__restrict__ array2, uint32_t length2, + uint16_t *__restrict__ output) { + if ((length1 < 8) || (length2 < 8)) { + return xor_uint16(array1, length1, array2, length2, output); + } + __m128i vA, vB, V, vecMin, vecMax; + __m128i laststore; + uint16_t *initoutput = output; + uint32_t len1 = length1 / 8; + uint32_t len2 = length2 / 8; + uint32_t pos1 = 0; + uint32_t pos2 = 0; + // we start the machine + vA = _mm_lddqu_si128((const __m128i *)array1 + pos1); + pos1++; + vB = _mm_lddqu_si128((const __m128i *)array2 + pos2); + pos2++; + sse_merge(&vA, &vB, &vecMin, &vecMax); + laststore = _mm_set1_epi16(-1); + uint16_t buffer[17]; + output += store_unique_xor(laststore, vecMin, output); + + laststore = vecMin; + if ((pos1 < len1) && (pos2 < len2)) { + uint16_t curA, curB; + curA = array1[8 * pos1]; + curB = array2[8 * pos2]; + while (true) { + if (curA <= curB) { + V = _mm_lddqu_si128((const __m128i *)array1 + pos1); + pos1++; + if (pos1 < len1) { + curA = array1[8 * pos1]; + } else { + break; + } + } else { + V = _mm_lddqu_si128((const __m128i *)array2 + pos2); + pos2++; + if (pos2 < len2) { + curB = array2[8 * pos2]; + } else { + break; + } + } + sse_merge(&V, &vecMax, &vecMin, &vecMax); + // conditionally stores the last value of laststore as well as all + // but the + // last value of vecMin + output += store_unique_xor(laststore, vecMin, output); + laststore = vecMin; + } + sse_merge(&V, &vecMax, &vecMin, &vecMax); + // conditionally stores the last value of laststore as well as all but + // the + // last value of vecMin + output += store_unique_xor(laststore, vecMin, output); + laststore = vecMin; + } + uint32_t len = (uint32_t)(output - initoutput); + + // we finish the rest off using a scalar algorithm + // could be improved? + // conditionally stores the last value of laststore as well as all but the + // last value of vecMax, + // we store to "buffer" + int leftoversize = store_unique_xor(laststore, vecMax, buffer); + uint16_t vec7 = _mm_extract_epi16(vecMax, 7); + uint16_t vec6 = _mm_extract_epi16(vecMax, 6); + if (vec7 != vec6) buffer[leftoversize++] = vec7; + if (pos1 == len1) { + memcpy(buffer + leftoversize, array1 + 8 * pos1, + (length1 - 8 * len1) * sizeof(uint16_t)); + leftoversize += length1 - 8 * len1; + if (leftoversize == 0) { // trivial case + memcpy(output, array2 + 8 * pos2, + (length2 - 8 * pos2) * sizeof(uint16_t)); + len += (length2 - 8 * pos2); + } else { + qsort(buffer, leftoversize, sizeof(uint16_t), uint16_compare); + leftoversize = unique_xor(buffer, leftoversize); + len += xor_uint16(buffer, leftoversize, array2 + 8 * pos2, + length2 - 8 * pos2, output); + } + } else { + memcpy(buffer + leftoversize, array2 + 8 * pos2, + (length2 - 8 * len2) * sizeof(uint16_t)); + leftoversize += length2 - 8 * len2; + if (leftoversize == 0) { // trivial case + memcpy(output, array1 + 8 * pos1, + (length1 - 8 * pos1) * sizeof(uint16_t)); + len += (length1 - 8 * pos1); + } else { + qsort(buffer, leftoversize, sizeof(uint16_t), uint16_compare); + leftoversize = unique_xor(buffer, leftoversize); + len += xor_uint16(buffer, leftoversize, array1 + 8 * pos1, + length1 - 8 * pos1, output); + } + } + return len; +} + +/** + * End of SIMD 16-bit XOR code + */ + +#endif // USESSE4 + +size_t union_uint32(const uint32_t *set_1, size_t size_1, const uint32_t *set_2, + size_t size_2, uint32_t *buffer) { + size_t pos = 0, idx_1 = 0, idx_2 = 0; + + if (0 == size_2) { + memmove(buffer, set_1, size_1 * sizeof(uint32_t)); + return size_1; + } + if (0 == size_1) { + memmove(buffer, set_2, size_2 * sizeof(uint32_t)); + return size_2; + } + + uint32_t val_1 = set_1[idx_1], val_2 = set_2[idx_2]; + + while (true) { + if (val_1 < val_2) { + buffer[pos++] = val_1; + ++idx_1; + if (idx_1 >= size_1) break; + val_1 = set_1[idx_1]; + } else if (val_2 < val_1) { + buffer[pos++] = val_2; + ++idx_2; + if (idx_2 >= size_2) break; + val_2 = set_2[idx_2]; + } else { + buffer[pos++] = val_1; + ++idx_1; + ++idx_2; + if (idx_1 >= size_1 || idx_2 >= size_2) break; + val_1 = set_1[idx_1]; + val_2 = set_2[idx_2]; + } + } + + if (idx_1 < size_1) { + const size_t n_elems = size_1 - idx_1; + memmove(buffer + pos, set_1 + idx_1, n_elems * sizeof(uint32_t)); + pos += n_elems; + } else if (idx_2 < size_2) { + const size_t n_elems = size_2 - idx_2; + memmove(buffer + pos, set_2 + idx_2, n_elems * sizeof(uint32_t)); + pos += n_elems; + } + + return pos; +} + +size_t union_uint32_card(const uint32_t *set_1, size_t size_1, + const uint32_t *set_2, size_t size_2) { + size_t pos = 0, idx_1 = 0, idx_2 = 0; + + if (0 == size_2) { + return size_1; + } + if (0 == size_1) { + return size_2; + } + + uint32_t val_1 = set_1[idx_1], val_2 = set_2[idx_2]; + + while (true) { + if (val_1 < val_2) { + ++idx_1; + ++pos; + if (idx_1 >= size_1) break; + val_1 = set_1[idx_1]; + } else if (val_2 < val_1) { + ++idx_2; + ++pos; + if (idx_2 >= size_2) break; + val_2 = set_2[idx_2]; + } else { + ++idx_1; + ++idx_2; + ++pos; + if (idx_1 >= size_1 || idx_2 >= size_2) break; + val_1 = set_1[idx_1]; + val_2 = set_2[idx_2]; + } + } + + if (idx_1 < size_1) { + const size_t n_elems = size_1 - idx_1; + pos += n_elems; + } else if (idx_2 < size_2) { + const size_t n_elems = size_2 - idx_2; + pos += n_elems; + } + return pos; +} + + + +size_t fast_union_uint16(const uint16_t *set_1, size_t size_1, const uint16_t *set_2, + size_t size_2, uint16_t *buffer) { +#ifdef ROARING_VECTOR_OPERATIONS_ENABLED + // compute union with smallest array first + if (size_1 < size_2) { + return union_vector16(set_1, (uint32_t)size_1, + set_2, (uint32_t)size_2, buffer); + } else { + return union_vector16(set_2, (uint32_t)size_2, + set_1, (uint32_t)size_1, buffer); + } +#else + // compute union with smallest array first + if (size_1 < size_2) { + return union_uint16( + set_1, size_1, set_2, size_2, buffer); + } else { + return union_uint16( + set_2, size_2, set_1, size_1, buffer); + } +#endif +} + +bool memequals(const void *s1, const void *s2, size_t n) { + if (n == 0) { + return true; + } +#ifdef USEAVX + const uint8_t *ptr1 = (const uint8_t *)s1; + const uint8_t *ptr2 = (const uint8_t *)s2; + const uint8_t *end1 = ptr1 + n; + const uint8_t *end8 = ptr1 + n/8*8; + const uint8_t *end32 = ptr1 + n/32*32; + + while (ptr1 < end32) { + __m256i r1 = _mm256_loadu_si256((const __m256i*)ptr1); + __m256i r2 = _mm256_loadu_si256((const __m256i*)ptr2); + int mask = _mm256_movemask_epi8(_mm256_cmpeq_epi8(r1, r2)); + if ((uint32_t)mask != UINT32_MAX) { + return false; + } + ptr1 += 32; + ptr2 += 32; + } + + while (ptr1 < end8) { + uint64_t v1 = *((const uint64_t*)ptr1); + uint64_t v2 = *((const uint64_t*)ptr2); + if (v1 != v2) { + return false; + } + ptr1 += 8; + ptr2 += 8; + } + + while (ptr1 < end1) { + if (*ptr1 != *ptr2) { + return false; + } + ptr1++; + ptr2++; + } + + return true; +#else + return memcmp(s1, s2, n) == 0; +#endif +} +/* end file src/array_util.c */ +/* begin file src/bitset_util.c */ +#include +#include +#include +#include +#include + + +#ifdef IS_X64 +static uint8_t lengthTable[256] = { + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3, 4, + 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, + 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, + 4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, + 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, + 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8}; +#endif + +#ifdef USEAVX +ALIGNED(32) +static uint32_t vecDecodeTable[256][8] = { + {0, 0, 0, 0, 0, 0, 0, 0}, /* 0x00 (00000000) */ + {1, 0, 0, 0, 0, 0, 0, 0}, /* 0x01 (00000001) */ + {2, 0, 0, 0, 0, 0, 0, 0}, /* 0x02 (00000010) */ + {1, 2, 0, 0, 0, 0, 0, 0}, /* 0x03 (00000011) */ + {3, 0, 0, 0, 0, 0, 0, 0}, /* 0x04 (00000100) */ + {1, 3, 0, 0, 0, 0, 0, 0}, /* 0x05 (00000101) */ + {2, 3, 0, 0, 0, 0, 0, 0}, /* 0x06 (00000110) */ + {1, 2, 3, 0, 0, 0, 0, 0}, /* 0x07 (00000111) */ + {4, 0, 0, 0, 0, 0, 0, 0}, /* 0x08 (00001000) */ + {1, 4, 0, 0, 0, 0, 0, 0}, /* 0x09 (00001001) */ + {2, 4, 0, 0, 0, 0, 0, 0}, /* 0x0A (00001010) */ + {1, 2, 4, 0, 0, 0, 0, 0}, /* 0x0B (00001011) */ + {3, 4, 0, 0, 0, 0, 0, 0}, /* 0x0C (00001100) */ + {1, 3, 4, 0, 0, 0, 0, 0}, /* 0x0D (00001101) */ + {2, 3, 4, 0, 0, 0, 0, 0}, /* 0x0E (00001110) */ + {1, 2, 3, 4, 0, 0, 0, 0}, /* 0x0F (00001111) */ + {5, 0, 0, 0, 0, 0, 0, 0}, /* 0x10 (00010000) */ + {1, 5, 0, 0, 0, 0, 0, 0}, /* 0x11 (00010001) */ + {2, 5, 0, 0, 0, 0, 0, 0}, /* 0x12 (00010010) */ + {1, 2, 5, 0, 0, 0, 0, 0}, /* 0x13 (00010011) */ + {3, 5, 0, 0, 0, 0, 0, 0}, /* 0x14 (00010100) */ + {1, 3, 5, 0, 0, 0, 0, 0}, /* 0x15 (00010101) */ + {2, 3, 5, 0, 0, 0, 0, 0}, /* 0x16 (00010110) */ + {1, 2, 3, 5, 0, 0, 0, 0}, /* 0x17 (00010111) */ + {4, 5, 0, 0, 0, 0, 0, 0}, /* 0x18 (00011000) */ + {1, 4, 5, 0, 0, 0, 0, 0}, /* 0x19 (00011001) */ + {2, 4, 5, 0, 0, 0, 0, 0}, /* 0x1A (00011010) */ + {1, 2, 4, 5, 0, 0, 0, 0}, /* 0x1B (00011011) */ + {3, 4, 5, 0, 0, 0, 0, 0}, /* 0x1C (00011100) */ + {1, 3, 4, 5, 0, 0, 0, 0}, /* 0x1D (00011101) */ + {2, 3, 4, 5, 0, 0, 0, 0}, /* 0x1E (00011110) */ + {1, 2, 3, 4, 5, 0, 0, 0}, /* 0x1F (00011111) */ + {6, 0, 0, 0, 0, 0, 0, 0}, /* 0x20 (00100000) */ + {1, 6, 0, 0, 0, 0, 0, 0}, /* 0x21 (00100001) */ + {2, 6, 0, 0, 0, 0, 0, 0}, /* 0x22 (00100010) */ + {1, 2, 6, 0, 0, 0, 0, 0}, /* 0x23 (00100011) */ + {3, 6, 0, 0, 0, 0, 0, 0}, /* 0x24 (00100100) */ + {1, 3, 6, 0, 0, 0, 0, 0}, /* 0x25 (00100101) */ + {2, 3, 6, 0, 0, 0, 0, 0}, /* 0x26 (00100110) */ + {1, 2, 3, 6, 0, 0, 0, 0}, /* 0x27 (00100111) */ + {4, 6, 0, 0, 0, 0, 0, 0}, /* 0x28 (00101000) */ + {1, 4, 6, 0, 0, 0, 0, 0}, /* 0x29 (00101001) */ + {2, 4, 6, 0, 0, 0, 0, 0}, /* 0x2A (00101010) */ + {1, 2, 4, 6, 0, 0, 0, 0}, /* 0x2B (00101011) */ + {3, 4, 6, 0, 0, 0, 0, 0}, /* 0x2C (00101100) */ + {1, 3, 4, 6, 0, 0, 0, 0}, /* 0x2D (00101101) */ + {2, 3, 4, 6, 0, 0, 0, 0}, /* 0x2E (00101110) */ + {1, 2, 3, 4, 6, 0, 0, 0}, /* 0x2F (00101111) */ + {5, 6, 0, 0, 0, 0, 0, 0}, /* 0x30 (00110000) */ + {1, 5, 6, 0, 0, 0, 0, 0}, /* 0x31 (00110001) */ + {2, 5, 6, 0, 0, 0, 0, 0}, /* 0x32 (00110010) */ + {1, 2, 5, 6, 0, 0, 0, 0}, /* 0x33 (00110011) */ + {3, 5, 6, 0, 0, 0, 0, 0}, /* 0x34 (00110100) */ + {1, 3, 5, 6, 0, 0, 0, 0}, /* 0x35 (00110101) */ + {2, 3, 5, 6, 0, 0, 0, 0}, /* 0x36 (00110110) */ + {1, 2, 3, 5, 6, 0, 0, 0}, /* 0x37 (00110111) */ + {4, 5, 6, 0, 0, 0, 0, 0}, /* 0x38 (00111000) */ + {1, 4, 5, 6, 0, 0, 0, 0}, /* 0x39 (00111001) */ + {2, 4, 5, 6, 0, 0, 0, 0}, /* 0x3A (00111010) */ + {1, 2, 4, 5, 6, 0, 0, 0}, /* 0x3B (00111011) */ + {3, 4, 5, 6, 0, 0, 0, 0}, /* 0x3C (00111100) */ + {1, 3, 4, 5, 6, 0, 0, 0}, /* 0x3D (00111101) */ + {2, 3, 4, 5, 6, 0, 0, 0}, /* 0x3E (00111110) */ + {1, 2, 3, 4, 5, 6, 0, 0}, /* 0x3F (00111111) */ + {7, 0, 0, 0, 0, 0, 0, 0}, /* 0x40 (01000000) */ + {1, 7, 0, 0, 0, 0, 0, 0}, /* 0x41 (01000001) */ + {2, 7, 0, 0, 0, 0, 0, 0}, /* 0x42 (01000010) */ + {1, 2, 7, 0, 0, 0, 0, 0}, /* 0x43 (01000011) */ + {3, 7, 0, 0, 0, 0, 0, 0}, /* 0x44 (01000100) */ + {1, 3, 7, 0, 0, 0, 0, 0}, /* 0x45 (01000101) */ + {2, 3, 7, 0, 0, 0, 0, 0}, /* 0x46 (01000110) */ + {1, 2, 3, 7, 0, 0, 0, 0}, /* 0x47 (01000111) */ + {4, 7, 0, 0, 0, 0, 0, 0}, /* 0x48 (01001000) */ + {1, 4, 7, 0, 0, 0, 0, 0}, /* 0x49 (01001001) */ + {2, 4, 7, 0, 0, 0, 0, 0}, /* 0x4A (01001010) */ + {1, 2, 4, 7, 0, 0, 0, 0}, /* 0x4B (01001011) */ + {3, 4, 7, 0, 0, 0, 0, 0}, /* 0x4C (01001100) */ + {1, 3, 4, 7, 0, 0, 0, 0}, /* 0x4D (01001101) */ + {2, 3, 4, 7, 0, 0, 0, 0}, /* 0x4E (01001110) */ + {1, 2, 3, 4, 7, 0, 0, 0}, /* 0x4F (01001111) */ + {5, 7, 0, 0, 0, 0, 0, 0}, /* 0x50 (01010000) */ + {1, 5, 7, 0, 0, 0, 0, 0}, /* 0x51 (01010001) */ + {2, 5, 7, 0, 0, 0, 0, 0}, /* 0x52 (01010010) */ + {1, 2, 5, 7, 0, 0, 0, 0}, /* 0x53 (01010011) */ + {3, 5, 7, 0, 0, 0, 0, 0}, /* 0x54 (01010100) */ + {1, 3, 5, 7, 0, 0, 0, 0}, /* 0x55 (01010101) */ + {2, 3, 5, 7, 0, 0, 0, 0}, /* 0x56 (01010110) */ + {1, 2, 3, 5, 7, 0, 0, 0}, /* 0x57 (01010111) */ + {4, 5, 7, 0, 0, 0, 0, 0}, /* 0x58 (01011000) */ + {1, 4, 5, 7, 0, 0, 0, 0}, /* 0x59 (01011001) */ + {2, 4, 5, 7, 0, 0, 0, 0}, /* 0x5A (01011010) */ + {1, 2, 4, 5, 7, 0, 0, 0}, /* 0x5B (01011011) */ + {3, 4, 5, 7, 0, 0, 0, 0}, /* 0x5C (01011100) */ + {1, 3, 4, 5, 7, 0, 0, 0}, /* 0x5D (01011101) */ + {2, 3, 4, 5, 7, 0, 0, 0}, /* 0x5E (01011110) */ + {1, 2, 3, 4, 5, 7, 0, 0}, /* 0x5F (01011111) */ + {6, 7, 0, 0, 0, 0, 0, 0}, /* 0x60 (01100000) */ + {1, 6, 7, 0, 0, 0, 0, 0}, /* 0x61 (01100001) */ + {2, 6, 7, 0, 0, 0, 0, 0}, /* 0x62 (01100010) */ + {1, 2, 6, 7, 0, 0, 0, 0}, /* 0x63 (01100011) */ + {3, 6, 7, 0, 0, 0, 0, 0}, /* 0x64 (01100100) */ + {1, 3, 6, 7, 0, 0, 0, 0}, /* 0x65 (01100101) */ + {2, 3, 6, 7, 0, 0, 0, 0}, /* 0x66 (01100110) */ + {1, 2, 3, 6, 7, 0, 0, 0}, /* 0x67 (01100111) */ + {4, 6, 7, 0, 0, 0, 0, 0}, /* 0x68 (01101000) */ + {1, 4, 6, 7, 0, 0, 0, 0}, /* 0x69 (01101001) */ + {2, 4, 6, 7, 0, 0, 0, 0}, /* 0x6A (01101010) */ + {1, 2, 4, 6, 7, 0, 0, 0}, /* 0x6B (01101011) */ + {3, 4, 6, 7, 0, 0, 0, 0}, /* 0x6C (01101100) */ + {1, 3, 4, 6, 7, 0, 0, 0}, /* 0x6D (01101101) */ + {2, 3, 4, 6, 7, 0, 0, 0}, /* 0x6E (01101110) */ + {1, 2, 3, 4, 6, 7, 0, 0}, /* 0x6F (01101111) */ + {5, 6, 7, 0, 0, 0, 0, 0}, /* 0x70 (01110000) */ + {1, 5, 6, 7, 0, 0, 0, 0}, /* 0x71 (01110001) */ + {2, 5, 6, 7, 0, 0, 0, 0}, /* 0x72 (01110010) */ + {1, 2, 5, 6, 7, 0, 0, 0}, /* 0x73 (01110011) */ + {3, 5, 6, 7, 0, 0, 0, 0}, /* 0x74 (01110100) */ + {1, 3, 5, 6, 7, 0, 0, 0}, /* 0x75 (01110101) */ + {2, 3, 5, 6, 7, 0, 0, 0}, /* 0x76 (01110110) */ + {1, 2, 3, 5, 6, 7, 0, 0}, /* 0x77 (01110111) */ + {4, 5, 6, 7, 0, 0, 0, 0}, /* 0x78 (01111000) */ + {1, 4, 5, 6, 7, 0, 0, 0}, /* 0x79 (01111001) */ + {2, 4, 5, 6, 7, 0, 0, 0}, /* 0x7A (01111010) */ + {1, 2, 4, 5, 6, 7, 0, 0}, /* 0x7B (01111011) */ + {3, 4, 5, 6, 7, 0, 0, 0}, /* 0x7C (01111100) */ + {1, 3, 4, 5, 6, 7, 0, 0}, /* 0x7D (01111101) */ + {2, 3, 4, 5, 6, 7, 0, 0}, /* 0x7E (01111110) */ + {1, 2, 3, 4, 5, 6, 7, 0}, /* 0x7F (01111111) */ + {8, 0, 0, 0, 0, 0, 0, 0}, /* 0x80 (10000000) */ + {1, 8, 0, 0, 0, 0, 0, 0}, /* 0x81 (10000001) */ + {2, 8, 0, 0, 0, 0, 0, 0}, /* 0x82 (10000010) */ + {1, 2, 8, 0, 0, 0, 0, 0}, /* 0x83 (10000011) */ + {3, 8, 0, 0, 0, 0, 0, 0}, /* 0x84 (10000100) */ + {1, 3, 8, 0, 0, 0, 0, 0}, /* 0x85 (10000101) */ + {2, 3, 8, 0, 0, 0, 0, 0}, /* 0x86 (10000110) */ + {1, 2, 3, 8, 0, 0, 0, 0}, /* 0x87 (10000111) */ + {4, 8, 0, 0, 0, 0, 0, 0}, /* 0x88 (10001000) */ + {1, 4, 8, 0, 0, 0, 0, 0}, /* 0x89 (10001001) */ + {2, 4, 8, 0, 0, 0, 0, 0}, /* 0x8A (10001010) */ + {1, 2, 4, 8, 0, 0, 0, 0}, /* 0x8B (10001011) */ + {3, 4, 8, 0, 0, 0, 0, 0}, /* 0x8C (10001100) */ + {1, 3, 4, 8, 0, 0, 0, 0}, /* 0x8D (10001101) */ + {2, 3, 4, 8, 0, 0, 0, 0}, /* 0x8E (10001110) */ + {1, 2, 3, 4, 8, 0, 0, 0}, /* 0x8F (10001111) */ + {5, 8, 0, 0, 0, 0, 0, 0}, /* 0x90 (10010000) */ + {1, 5, 8, 0, 0, 0, 0, 0}, /* 0x91 (10010001) */ + {2, 5, 8, 0, 0, 0, 0, 0}, /* 0x92 (10010010) */ + {1, 2, 5, 8, 0, 0, 0, 0}, /* 0x93 (10010011) */ + {3, 5, 8, 0, 0, 0, 0, 0}, /* 0x94 (10010100) */ + {1, 3, 5, 8, 0, 0, 0, 0}, /* 0x95 (10010101) */ + {2, 3, 5, 8, 0, 0, 0, 0}, /* 0x96 (10010110) */ + {1, 2, 3, 5, 8, 0, 0, 0}, /* 0x97 (10010111) */ + {4, 5, 8, 0, 0, 0, 0, 0}, /* 0x98 (10011000) */ + {1, 4, 5, 8, 0, 0, 0, 0}, /* 0x99 (10011001) */ + {2, 4, 5, 8, 0, 0, 0, 0}, /* 0x9A (10011010) */ + {1, 2, 4, 5, 8, 0, 0, 0}, /* 0x9B (10011011) */ + {3, 4, 5, 8, 0, 0, 0, 0}, /* 0x9C (10011100) */ + {1, 3, 4, 5, 8, 0, 0, 0}, /* 0x9D (10011101) */ + {2, 3, 4, 5, 8, 0, 0, 0}, /* 0x9E (10011110) */ + {1, 2, 3, 4, 5, 8, 0, 0}, /* 0x9F (10011111) */ + {6, 8, 0, 0, 0, 0, 0, 0}, /* 0xA0 (10100000) */ + {1, 6, 8, 0, 0, 0, 0, 0}, /* 0xA1 (10100001) */ + {2, 6, 8, 0, 0, 0, 0, 0}, /* 0xA2 (10100010) */ + {1, 2, 6, 8, 0, 0, 0, 0}, /* 0xA3 (10100011) */ + {3, 6, 8, 0, 0, 0, 0, 0}, /* 0xA4 (10100100) */ + {1, 3, 6, 8, 0, 0, 0, 0}, /* 0xA5 (10100101) */ + {2, 3, 6, 8, 0, 0, 0, 0}, /* 0xA6 (10100110) */ + {1, 2, 3, 6, 8, 0, 0, 0}, /* 0xA7 (10100111) */ + {4, 6, 8, 0, 0, 0, 0, 0}, /* 0xA8 (10101000) */ + {1, 4, 6, 8, 0, 0, 0, 0}, /* 0xA9 (10101001) */ + {2, 4, 6, 8, 0, 0, 0, 0}, /* 0xAA (10101010) */ + {1, 2, 4, 6, 8, 0, 0, 0}, /* 0xAB (10101011) */ + {3, 4, 6, 8, 0, 0, 0, 0}, /* 0xAC (10101100) */ + {1, 3, 4, 6, 8, 0, 0, 0}, /* 0xAD (10101101) */ + {2, 3, 4, 6, 8, 0, 0, 0}, /* 0xAE (10101110) */ + {1, 2, 3, 4, 6, 8, 0, 0}, /* 0xAF (10101111) */ + {5, 6, 8, 0, 0, 0, 0, 0}, /* 0xB0 (10110000) */ + {1, 5, 6, 8, 0, 0, 0, 0}, /* 0xB1 (10110001) */ + {2, 5, 6, 8, 0, 0, 0, 0}, /* 0xB2 (10110010) */ + {1, 2, 5, 6, 8, 0, 0, 0}, /* 0xB3 (10110011) */ + {3, 5, 6, 8, 0, 0, 0, 0}, /* 0xB4 (10110100) */ + {1, 3, 5, 6, 8, 0, 0, 0}, /* 0xB5 (10110101) */ + {2, 3, 5, 6, 8, 0, 0, 0}, /* 0xB6 (10110110) */ + {1, 2, 3, 5, 6, 8, 0, 0}, /* 0xB7 (10110111) */ + {4, 5, 6, 8, 0, 0, 0, 0}, /* 0xB8 (10111000) */ + {1, 4, 5, 6, 8, 0, 0, 0}, /* 0xB9 (10111001) */ + {2, 4, 5, 6, 8, 0, 0, 0}, /* 0xBA (10111010) */ + {1, 2, 4, 5, 6, 8, 0, 0}, /* 0xBB (10111011) */ + {3, 4, 5, 6, 8, 0, 0, 0}, /* 0xBC (10111100) */ + {1, 3, 4, 5, 6, 8, 0, 0}, /* 0xBD (10111101) */ + {2, 3, 4, 5, 6, 8, 0, 0}, /* 0xBE (10111110) */ + {1, 2, 3, 4, 5, 6, 8, 0}, /* 0xBF (10111111) */ + {7, 8, 0, 0, 0, 0, 0, 0}, /* 0xC0 (11000000) */ + {1, 7, 8, 0, 0, 0, 0, 0}, /* 0xC1 (11000001) */ + {2, 7, 8, 0, 0, 0, 0, 0}, /* 0xC2 (11000010) */ + {1, 2, 7, 8, 0, 0, 0, 0}, /* 0xC3 (11000011) */ + {3, 7, 8, 0, 0, 0, 0, 0}, /* 0xC4 (11000100) */ + {1, 3, 7, 8, 0, 0, 0, 0}, /* 0xC5 (11000101) */ + {2, 3, 7, 8, 0, 0, 0, 0}, /* 0xC6 (11000110) */ + {1, 2, 3, 7, 8, 0, 0, 0}, /* 0xC7 (11000111) */ + {4, 7, 8, 0, 0, 0, 0, 0}, /* 0xC8 (11001000) */ + {1, 4, 7, 8, 0, 0, 0, 0}, /* 0xC9 (11001001) */ + {2, 4, 7, 8, 0, 0, 0, 0}, /* 0xCA (11001010) */ + {1, 2, 4, 7, 8, 0, 0, 0}, /* 0xCB (11001011) */ + {3, 4, 7, 8, 0, 0, 0, 0}, /* 0xCC (11001100) */ + {1, 3, 4, 7, 8, 0, 0, 0}, /* 0xCD (11001101) */ + {2, 3, 4, 7, 8, 0, 0, 0}, /* 0xCE (11001110) */ + {1, 2, 3, 4, 7, 8, 0, 0}, /* 0xCF (11001111) */ + {5, 7, 8, 0, 0, 0, 0, 0}, /* 0xD0 (11010000) */ + {1, 5, 7, 8, 0, 0, 0, 0}, /* 0xD1 (11010001) */ + {2, 5, 7, 8, 0, 0, 0, 0}, /* 0xD2 (11010010) */ + {1, 2, 5, 7, 8, 0, 0, 0}, /* 0xD3 (11010011) */ + {3, 5, 7, 8, 0, 0, 0, 0}, /* 0xD4 (11010100) */ + {1, 3, 5, 7, 8, 0, 0, 0}, /* 0xD5 (11010101) */ + {2, 3, 5, 7, 8, 0, 0, 0}, /* 0xD6 (11010110) */ + {1, 2, 3, 5, 7, 8, 0, 0}, /* 0xD7 (11010111) */ + {4, 5, 7, 8, 0, 0, 0, 0}, /* 0xD8 (11011000) */ + {1, 4, 5, 7, 8, 0, 0, 0}, /* 0xD9 (11011001) */ + {2, 4, 5, 7, 8, 0, 0, 0}, /* 0xDA (11011010) */ + {1, 2, 4, 5, 7, 8, 0, 0}, /* 0xDB (11011011) */ + {3, 4, 5, 7, 8, 0, 0, 0}, /* 0xDC (11011100) */ + {1, 3, 4, 5, 7, 8, 0, 0}, /* 0xDD (11011101) */ + {2, 3, 4, 5, 7, 8, 0, 0}, /* 0xDE (11011110) */ + {1, 2, 3, 4, 5, 7, 8, 0}, /* 0xDF (11011111) */ + {6, 7, 8, 0, 0, 0, 0, 0}, /* 0xE0 (11100000) */ + {1, 6, 7, 8, 0, 0, 0, 0}, /* 0xE1 (11100001) */ + {2, 6, 7, 8, 0, 0, 0, 0}, /* 0xE2 (11100010) */ + {1, 2, 6, 7, 8, 0, 0, 0}, /* 0xE3 (11100011) */ + {3, 6, 7, 8, 0, 0, 0, 0}, /* 0xE4 (11100100) */ + {1, 3, 6, 7, 8, 0, 0, 0}, /* 0xE5 (11100101) */ + {2, 3, 6, 7, 8, 0, 0, 0}, /* 0xE6 (11100110) */ + {1, 2, 3, 6, 7, 8, 0, 0}, /* 0xE7 (11100111) */ + {4, 6, 7, 8, 0, 0, 0, 0}, /* 0xE8 (11101000) */ + {1, 4, 6, 7, 8, 0, 0, 0}, /* 0xE9 (11101001) */ + {2, 4, 6, 7, 8, 0, 0, 0}, /* 0xEA (11101010) */ + {1, 2, 4, 6, 7, 8, 0, 0}, /* 0xEB (11101011) */ + {3, 4, 6, 7, 8, 0, 0, 0}, /* 0xEC (11101100) */ + {1, 3, 4, 6, 7, 8, 0, 0}, /* 0xED (11101101) */ + {2, 3, 4, 6, 7, 8, 0, 0}, /* 0xEE (11101110) */ + {1, 2, 3, 4, 6, 7, 8, 0}, /* 0xEF (11101111) */ + {5, 6, 7, 8, 0, 0, 0, 0}, /* 0xF0 (11110000) */ + {1, 5, 6, 7, 8, 0, 0, 0}, /* 0xF1 (11110001) */ + {2, 5, 6, 7, 8, 0, 0, 0}, /* 0xF2 (11110010) */ + {1, 2, 5, 6, 7, 8, 0, 0}, /* 0xF3 (11110011) */ + {3, 5, 6, 7, 8, 0, 0, 0}, /* 0xF4 (11110100) */ + {1, 3, 5, 6, 7, 8, 0, 0}, /* 0xF5 (11110101) */ + {2, 3, 5, 6, 7, 8, 0, 0}, /* 0xF6 (11110110) */ + {1, 2, 3, 5, 6, 7, 8, 0}, /* 0xF7 (11110111) */ + {4, 5, 6, 7, 8, 0, 0, 0}, /* 0xF8 (11111000) */ + {1, 4, 5, 6, 7, 8, 0, 0}, /* 0xF9 (11111001) */ + {2, 4, 5, 6, 7, 8, 0, 0}, /* 0xFA (11111010) */ + {1, 2, 4, 5, 6, 7, 8, 0}, /* 0xFB (11111011) */ + {3, 4, 5, 6, 7, 8, 0, 0}, /* 0xFC (11111100) */ + {1, 3, 4, 5, 6, 7, 8, 0}, /* 0xFD (11111101) */ + {2, 3, 4, 5, 6, 7, 8, 0}, /* 0xFE (11111110) */ + {1, 2, 3, 4, 5, 6, 7, 8} /* 0xFF (11111111) */ +}; + +#endif // #ifdef USEAVX + +#ifdef IS_X64 +// same as vecDecodeTable but in 16 bits +ALIGNED(32) +static uint16_t vecDecodeTable_uint16[256][8] = { + {0, 0, 0, 0, 0, 0, 0, 0}, /* 0x00 (00000000) */ + {1, 0, 0, 0, 0, 0, 0, 0}, /* 0x01 (00000001) */ + {2, 0, 0, 0, 0, 0, 0, 0}, /* 0x02 (00000010) */ + {1, 2, 0, 0, 0, 0, 0, 0}, /* 0x03 (00000011) */ + {3, 0, 0, 0, 0, 0, 0, 0}, /* 0x04 (00000100) */ + {1, 3, 0, 0, 0, 0, 0, 0}, /* 0x05 (00000101) */ + {2, 3, 0, 0, 0, 0, 0, 0}, /* 0x06 (00000110) */ + {1, 2, 3, 0, 0, 0, 0, 0}, /* 0x07 (00000111) */ + {4, 0, 0, 0, 0, 0, 0, 0}, /* 0x08 (00001000) */ + {1, 4, 0, 0, 0, 0, 0, 0}, /* 0x09 (00001001) */ + {2, 4, 0, 0, 0, 0, 0, 0}, /* 0x0A (00001010) */ + {1, 2, 4, 0, 0, 0, 0, 0}, /* 0x0B (00001011) */ + {3, 4, 0, 0, 0, 0, 0, 0}, /* 0x0C (00001100) */ + {1, 3, 4, 0, 0, 0, 0, 0}, /* 0x0D (00001101) */ + {2, 3, 4, 0, 0, 0, 0, 0}, /* 0x0E (00001110) */ + {1, 2, 3, 4, 0, 0, 0, 0}, /* 0x0F (00001111) */ + {5, 0, 0, 0, 0, 0, 0, 0}, /* 0x10 (00010000) */ + {1, 5, 0, 0, 0, 0, 0, 0}, /* 0x11 (00010001) */ + {2, 5, 0, 0, 0, 0, 0, 0}, /* 0x12 (00010010) */ + {1, 2, 5, 0, 0, 0, 0, 0}, /* 0x13 (00010011) */ + {3, 5, 0, 0, 0, 0, 0, 0}, /* 0x14 (00010100) */ + {1, 3, 5, 0, 0, 0, 0, 0}, /* 0x15 (00010101) */ + {2, 3, 5, 0, 0, 0, 0, 0}, /* 0x16 (00010110) */ + {1, 2, 3, 5, 0, 0, 0, 0}, /* 0x17 (00010111) */ + {4, 5, 0, 0, 0, 0, 0, 0}, /* 0x18 (00011000) */ + {1, 4, 5, 0, 0, 0, 0, 0}, /* 0x19 (00011001) */ + {2, 4, 5, 0, 0, 0, 0, 0}, /* 0x1A (00011010) */ + {1, 2, 4, 5, 0, 0, 0, 0}, /* 0x1B (00011011) */ + {3, 4, 5, 0, 0, 0, 0, 0}, /* 0x1C (00011100) */ + {1, 3, 4, 5, 0, 0, 0, 0}, /* 0x1D (00011101) */ + {2, 3, 4, 5, 0, 0, 0, 0}, /* 0x1E (00011110) */ + {1, 2, 3, 4, 5, 0, 0, 0}, /* 0x1F (00011111) */ + {6, 0, 0, 0, 0, 0, 0, 0}, /* 0x20 (00100000) */ + {1, 6, 0, 0, 0, 0, 0, 0}, /* 0x21 (00100001) */ + {2, 6, 0, 0, 0, 0, 0, 0}, /* 0x22 (00100010) */ + {1, 2, 6, 0, 0, 0, 0, 0}, /* 0x23 (00100011) */ + {3, 6, 0, 0, 0, 0, 0, 0}, /* 0x24 (00100100) */ + {1, 3, 6, 0, 0, 0, 0, 0}, /* 0x25 (00100101) */ + {2, 3, 6, 0, 0, 0, 0, 0}, /* 0x26 (00100110) */ + {1, 2, 3, 6, 0, 0, 0, 0}, /* 0x27 (00100111) */ + {4, 6, 0, 0, 0, 0, 0, 0}, /* 0x28 (00101000) */ + {1, 4, 6, 0, 0, 0, 0, 0}, /* 0x29 (00101001) */ + {2, 4, 6, 0, 0, 0, 0, 0}, /* 0x2A (00101010) */ + {1, 2, 4, 6, 0, 0, 0, 0}, /* 0x2B (00101011) */ + {3, 4, 6, 0, 0, 0, 0, 0}, /* 0x2C (00101100) */ + {1, 3, 4, 6, 0, 0, 0, 0}, /* 0x2D (00101101) */ + {2, 3, 4, 6, 0, 0, 0, 0}, /* 0x2E (00101110) */ + {1, 2, 3, 4, 6, 0, 0, 0}, /* 0x2F (00101111) */ + {5, 6, 0, 0, 0, 0, 0, 0}, /* 0x30 (00110000) */ + {1, 5, 6, 0, 0, 0, 0, 0}, /* 0x31 (00110001) */ + {2, 5, 6, 0, 0, 0, 0, 0}, /* 0x32 (00110010) */ + {1, 2, 5, 6, 0, 0, 0, 0}, /* 0x33 (00110011) */ + {3, 5, 6, 0, 0, 0, 0, 0}, /* 0x34 (00110100) */ + {1, 3, 5, 6, 0, 0, 0, 0}, /* 0x35 (00110101) */ + {2, 3, 5, 6, 0, 0, 0, 0}, /* 0x36 (00110110) */ + {1, 2, 3, 5, 6, 0, 0, 0}, /* 0x37 (00110111) */ + {4, 5, 6, 0, 0, 0, 0, 0}, /* 0x38 (00111000) */ + {1, 4, 5, 6, 0, 0, 0, 0}, /* 0x39 (00111001) */ + {2, 4, 5, 6, 0, 0, 0, 0}, /* 0x3A (00111010) */ + {1, 2, 4, 5, 6, 0, 0, 0}, /* 0x3B (00111011) */ + {3, 4, 5, 6, 0, 0, 0, 0}, /* 0x3C (00111100) */ + {1, 3, 4, 5, 6, 0, 0, 0}, /* 0x3D (00111101) */ + {2, 3, 4, 5, 6, 0, 0, 0}, /* 0x3E (00111110) */ + {1, 2, 3, 4, 5, 6, 0, 0}, /* 0x3F (00111111) */ + {7, 0, 0, 0, 0, 0, 0, 0}, /* 0x40 (01000000) */ + {1, 7, 0, 0, 0, 0, 0, 0}, /* 0x41 (01000001) */ + {2, 7, 0, 0, 0, 0, 0, 0}, /* 0x42 (01000010) */ + {1, 2, 7, 0, 0, 0, 0, 0}, /* 0x43 (01000011) */ + {3, 7, 0, 0, 0, 0, 0, 0}, /* 0x44 (01000100) */ + {1, 3, 7, 0, 0, 0, 0, 0}, /* 0x45 (01000101) */ + {2, 3, 7, 0, 0, 0, 0, 0}, /* 0x46 (01000110) */ + {1, 2, 3, 7, 0, 0, 0, 0}, /* 0x47 (01000111) */ + {4, 7, 0, 0, 0, 0, 0, 0}, /* 0x48 (01001000) */ + {1, 4, 7, 0, 0, 0, 0, 0}, /* 0x49 (01001001) */ + {2, 4, 7, 0, 0, 0, 0, 0}, /* 0x4A (01001010) */ + {1, 2, 4, 7, 0, 0, 0, 0}, /* 0x4B (01001011) */ + {3, 4, 7, 0, 0, 0, 0, 0}, /* 0x4C (01001100) */ + {1, 3, 4, 7, 0, 0, 0, 0}, /* 0x4D (01001101) */ + {2, 3, 4, 7, 0, 0, 0, 0}, /* 0x4E (01001110) */ + {1, 2, 3, 4, 7, 0, 0, 0}, /* 0x4F (01001111) */ + {5, 7, 0, 0, 0, 0, 0, 0}, /* 0x50 (01010000) */ + {1, 5, 7, 0, 0, 0, 0, 0}, /* 0x51 (01010001) */ + {2, 5, 7, 0, 0, 0, 0, 0}, /* 0x52 (01010010) */ + {1, 2, 5, 7, 0, 0, 0, 0}, /* 0x53 (01010011) */ + {3, 5, 7, 0, 0, 0, 0, 0}, /* 0x54 (01010100) */ + {1, 3, 5, 7, 0, 0, 0, 0}, /* 0x55 (01010101) */ + {2, 3, 5, 7, 0, 0, 0, 0}, /* 0x56 (01010110) */ + {1, 2, 3, 5, 7, 0, 0, 0}, /* 0x57 (01010111) */ + {4, 5, 7, 0, 0, 0, 0, 0}, /* 0x58 (01011000) */ + {1, 4, 5, 7, 0, 0, 0, 0}, /* 0x59 (01011001) */ + {2, 4, 5, 7, 0, 0, 0, 0}, /* 0x5A (01011010) */ + {1, 2, 4, 5, 7, 0, 0, 0}, /* 0x5B (01011011) */ + {3, 4, 5, 7, 0, 0, 0, 0}, /* 0x5C (01011100) */ + {1, 3, 4, 5, 7, 0, 0, 0}, /* 0x5D (01011101) */ + {2, 3, 4, 5, 7, 0, 0, 0}, /* 0x5E (01011110) */ + {1, 2, 3, 4, 5, 7, 0, 0}, /* 0x5F (01011111) */ + {6, 7, 0, 0, 0, 0, 0, 0}, /* 0x60 (01100000) */ + {1, 6, 7, 0, 0, 0, 0, 0}, /* 0x61 (01100001) */ + {2, 6, 7, 0, 0, 0, 0, 0}, /* 0x62 (01100010) */ + {1, 2, 6, 7, 0, 0, 0, 0}, /* 0x63 (01100011) */ + {3, 6, 7, 0, 0, 0, 0, 0}, /* 0x64 (01100100) */ + {1, 3, 6, 7, 0, 0, 0, 0}, /* 0x65 (01100101) */ + {2, 3, 6, 7, 0, 0, 0, 0}, /* 0x66 (01100110) */ + {1, 2, 3, 6, 7, 0, 0, 0}, /* 0x67 (01100111) */ + {4, 6, 7, 0, 0, 0, 0, 0}, /* 0x68 (01101000) */ + {1, 4, 6, 7, 0, 0, 0, 0}, /* 0x69 (01101001) */ + {2, 4, 6, 7, 0, 0, 0, 0}, /* 0x6A (01101010) */ + {1, 2, 4, 6, 7, 0, 0, 0}, /* 0x6B (01101011) */ + {3, 4, 6, 7, 0, 0, 0, 0}, /* 0x6C (01101100) */ + {1, 3, 4, 6, 7, 0, 0, 0}, /* 0x6D (01101101) */ + {2, 3, 4, 6, 7, 0, 0, 0}, /* 0x6E (01101110) */ + {1, 2, 3, 4, 6, 7, 0, 0}, /* 0x6F (01101111) */ + {5, 6, 7, 0, 0, 0, 0, 0}, /* 0x70 (01110000) */ + {1, 5, 6, 7, 0, 0, 0, 0}, /* 0x71 (01110001) */ + {2, 5, 6, 7, 0, 0, 0, 0}, /* 0x72 (01110010) */ + {1, 2, 5, 6, 7, 0, 0, 0}, /* 0x73 (01110011) */ + {3, 5, 6, 7, 0, 0, 0, 0}, /* 0x74 (01110100) */ + {1, 3, 5, 6, 7, 0, 0, 0}, /* 0x75 (01110101) */ + {2, 3, 5, 6, 7, 0, 0, 0}, /* 0x76 (01110110) */ + {1, 2, 3, 5, 6, 7, 0, 0}, /* 0x77 (01110111) */ + {4, 5, 6, 7, 0, 0, 0, 0}, /* 0x78 (01111000) */ + {1, 4, 5, 6, 7, 0, 0, 0}, /* 0x79 (01111001) */ + {2, 4, 5, 6, 7, 0, 0, 0}, /* 0x7A (01111010) */ + {1, 2, 4, 5, 6, 7, 0, 0}, /* 0x7B (01111011) */ + {3, 4, 5, 6, 7, 0, 0, 0}, /* 0x7C (01111100) */ + {1, 3, 4, 5, 6, 7, 0, 0}, /* 0x7D (01111101) */ + {2, 3, 4, 5, 6, 7, 0, 0}, /* 0x7E (01111110) */ + {1, 2, 3, 4, 5, 6, 7, 0}, /* 0x7F (01111111) */ + {8, 0, 0, 0, 0, 0, 0, 0}, /* 0x80 (10000000) */ + {1, 8, 0, 0, 0, 0, 0, 0}, /* 0x81 (10000001) */ + {2, 8, 0, 0, 0, 0, 0, 0}, /* 0x82 (10000010) */ + {1, 2, 8, 0, 0, 0, 0, 0}, /* 0x83 (10000011) */ + {3, 8, 0, 0, 0, 0, 0, 0}, /* 0x84 (10000100) */ + {1, 3, 8, 0, 0, 0, 0, 0}, /* 0x85 (10000101) */ + {2, 3, 8, 0, 0, 0, 0, 0}, /* 0x86 (10000110) */ + {1, 2, 3, 8, 0, 0, 0, 0}, /* 0x87 (10000111) */ + {4, 8, 0, 0, 0, 0, 0, 0}, /* 0x88 (10001000) */ + {1, 4, 8, 0, 0, 0, 0, 0}, /* 0x89 (10001001) */ + {2, 4, 8, 0, 0, 0, 0, 0}, /* 0x8A (10001010) */ + {1, 2, 4, 8, 0, 0, 0, 0}, /* 0x8B (10001011) */ + {3, 4, 8, 0, 0, 0, 0, 0}, /* 0x8C (10001100) */ + {1, 3, 4, 8, 0, 0, 0, 0}, /* 0x8D (10001101) */ + {2, 3, 4, 8, 0, 0, 0, 0}, /* 0x8E (10001110) */ + {1, 2, 3, 4, 8, 0, 0, 0}, /* 0x8F (10001111) */ + {5, 8, 0, 0, 0, 0, 0, 0}, /* 0x90 (10010000) */ + {1, 5, 8, 0, 0, 0, 0, 0}, /* 0x91 (10010001) */ + {2, 5, 8, 0, 0, 0, 0, 0}, /* 0x92 (10010010) */ + {1, 2, 5, 8, 0, 0, 0, 0}, /* 0x93 (10010011) */ + {3, 5, 8, 0, 0, 0, 0, 0}, /* 0x94 (10010100) */ + {1, 3, 5, 8, 0, 0, 0, 0}, /* 0x95 (10010101) */ + {2, 3, 5, 8, 0, 0, 0, 0}, /* 0x96 (10010110) */ + {1, 2, 3, 5, 8, 0, 0, 0}, /* 0x97 (10010111) */ + {4, 5, 8, 0, 0, 0, 0, 0}, /* 0x98 (10011000) */ + {1, 4, 5, 8, 0, 0, 0, 0}, /* 0x99 (10011001) */ + {2, 4, 5, 8, 0, 0, 0, 0}, /* 0x9A (10011010) */ + {1, 2, 4, 5, 8, 0, 0, 0}, /* 0x9B (10011011) */ + {3, 4, 5, 8, 0, 0, 0, 0}, /* 0x9C (10011100) */ + {1, 3, 4, 5, 8, 0, 0, 0}, /* 0x9D (10011101) */ + {2, 3, 4, 5, 8, 0, 0, 0}, /* 0x9E (10011110) */ + {1, 2, 3, 4, 5, 8, 0, 0}, /* 0x9F (10011111) */ + {6, 8, 0, 0, 0, 0, 0, 0}, /* 0xA0 (10100000) */ + {1, 6, 8, 0, 0, 0, 0, 0}, /* 0xA1 (10100001) */ + {2, 6, 8, 0, 0, 0, 0, 0}, /* 0xA2 (10100010) */ + {1, 2, 6, 8, 0, 0, 0, 0}, /* 0xA3 (10100011) */ + {3, 6, 8, 0, 0, 0, 0, 0}, /* 0xA4 (10100100) */ + {1, 3, 6, 8, 0, 0, 0, 0}, /* 0xA5 (10100101) */ + {2, 3, 6, 8, 0, 0, 0, 0}, /* 0xA6 (10100110) */ + {1, 2, 3, 6, 8, 0, 0, 0}, /* 0xA7 (10100111) */ + {4, 6, 8, 0, 0, 0, 0, 0}, /* 0xA8 (10101000) */ + {1, 4, 6, 8, 0, 0, 0, 0}, /* 0xA9 (10101001) */ + {2, 4, 6, 8, 0, 0, 0, 0}, /* 0xAA (10101010) */ + {1, 2, 4, 6, 8, 0, 0, 0}, /* 0xAB (10101011) */ + {3, 4, 6, 8, 0, 0, 0, 0}, /* 0xAC (10101100) */ + {1, 3, 4, 6, 8, 0, 0, 0}, /* 0xAD (10101101) */ + {2, 3, 4, 6, 8, 0, 0, 0}, /* 0xAE (10101110) */ + {1, 2, 3, 4, 6, 8, 0, 0}, /* 0xAF (10101111) */ + {5, 6, 8, 0, 0, 0, 0, 0}, /* 0xB0 (10110000) */ + {1, 5, 6, 8, 0, 0, 0, 0}, /* 0xB1 (10110001) */ + {2, 5, 6, 8, 0, 0, 0, 0}, /* 0xB2 (10110010) */ + {1, 2, 5, 6, 8, 0, 0, 0}, /* 0xB3 (10110011) */ + {3, 5, 6, 8, 0, 0, 0, 0}, /* 0xB4 (10110100) */ + {1, 3, 5, 6, 8, 0, 0, 0}, /* 0xB5 (10110101) */ + {2, 3, 5, 6, 8, 0, 0, 0}, /* 0xB6 (10110110) */ + {1, 2, 3, 5, 6, 8, 0, 0}, /* 0xB7 (10110111) */ + {4, 5, 6, 8, 0, 0, 0, 0}, /* 0xB8 (10111000) */ + {1, 4, 5, 6, 8, 0, 0, 0}, /* 0xB9 (10111001) */ + {2, 4, 5, 6, 8, 0, 0, 0}, /* 0xBA (10111010) */ + {1, 2, 4, 5, 6, 8, 0, 0}, /* 0xBB (10111011) */ + {3, 4, 5, 6, 8, 0, 0, 0}, /* 0xBC (10111100) */ + {1, 3, 4, 5, 6, 8, 0, 0}, /* 0xBD (10111101) */ + {2, 3, 4, 5, 6, 8, 0, 0}, /* 0xBE (10111110) */ + {1, 2, 3, 4, 5, 6, 8, 0}, /* 0xBF (10111111) */ + {7, 8, 0, 0, 0, 0, 0, 0}, /* 0xC0 (11000000) */ + {1, 7, 8, 0, 0, 0, 0, 0}, /* 0xC1 (11000001) */ + {2, 7, 8, 0, 0, 0, 0, 0}, /* 0xC2 (11000010) */ + {1, 2, 7, 8, 0, 0, 0, 0}, /* 0xC3 (11000011) */ + {3, 7, 8, 0, 0, 0, 0, 0}, /* 0xC4 (11000100) */ + {1, 3, 7, 8, 0, 0, 0, 0}, /* 0xC5 (11000101) */ + {2, 3, 7, 8, 0, 0, 0, 0}, /* 0xC6 (11000110) */ + {1, 2, 3, 7, 8, 0, 0, 0}, /* 0xC7 (11000111) */ + {4, 7, 8, 0, 0, 0, 0, 0}, /* 0xC8 (11001000) */ + {1, 4, 7, 8, 0, 0, 0, 0}, /* 0xC9 (11001001) */ + {2, 4, 7, 8, 0, 0, 0, 0}, /* 0xCA (11001010) */ + {1, 2, 4, 7, 8, 0, 0, 0}, /* 0xCB (11001011) */ + {3, 4, 7, 8, 0, 0, 0, 0}, /* 0xCC (11001100) */ + {1, 3, 4, 7, 8, 0, 0, 0}, /* 0xCD (11001101) */ + {2, 3, 4, 7, 8, 0, 0, 0}, /* 0xCE (11001110) */ + {1, 2, 3, 4, 7, 8, 0, 0}, /* 0xCF (11001111) */ + {5, 7, 8, 0, 0, 0, 0, 0}, /* 0xD0 (11010000) */ + {1, 5, 7, 8, 0, 0, 0, 0}, /* 0xD1 (11010001) */ + {2, 5, 7, 8, 0, 0, 0, 0}, /* 0xD2 (11010010) */ + {1, 2, 5, 7, 8, 0, 0, 0}, /* 0xD3 (11010011) */ + {3, 5, 7, 8, 0, 0, 0, 0}, /* 0xD4 (11010100) */ + {1, 3, 5, 7, 8, 0, 0, 0}, /* 0xD5 (11010101) */ + {2, 3, 5, 7, 8, 0, 0, 0}, /* 0xD6 (11010110) */ + {1, 2, 3, 5, 7, 8, 0, 0}, /* 0xD7 (11010111) */ + {4, 5, 7, 8, 0, 0, 0, 0}, /* 0xD8 (11011000) */ + {1, 4, 5, 7, 8, 0, 0, 0}, /* 0xD9 (11011001) */ + {2, 4, 5, 7, 8, 0, 0, 0}, /* 0xDA (11011010) */ + {1, 2, 4, 5, 7, 8, 0, 0}, /* 0xDB (11011011) */ + {3, 4, 5, 7, 8, 0, 0, 0}, /* 0xDC (11011100) */ + {1, 3, 4, 5, 7, 8, 0, 0}, /* 0xDD (11011101) */ + {2, 3, 4, 5, 7, 8, 0, 0}, /* 0xDE (11011110) */ + {1, 2, 3, 4, 5, 7, 8, 0}, /* 0xDF (11011111) */ + {6, 7, 8, 0, 0, 0, 0, 0}, /* 0xE0 (11100000) */ + {1, 6, 7, 8, 0, 0, 0, 0}, /* 0xE1 (11100001) */ + {2, 6, 7, 8, 0, 0, 0, 0}, /* 0xE2 (11100010) */ + {1, 2, 6, 7, 8, 0, 0, 0}, /* 0xE3 (11100011) */ + {3, 6, 7, 8, 0, 0, 0, 0}, /* 0xE4 (11100100) */ + {1, 3, 6, 7, 8, 0, 0, 0}, /* 0xE5 (11100101) */ + {2, 3, 6, 7, 8, 0, 0, 0}, /* 0xE6 (11100110) */ + {1, 2, 3, 6, 7, 8, 0, 0}, /* 0xE7 (11100111) */ + {4, 6, 7, 8, 0, 0, 0, 0}, /* 0xE8 (11101000) */ + {1, 4, 6, 7, 8, 0, 0, 0}, /* 0xE9 (11101001) */ + {2, 4, 6, 7, 8, 0, 0, 0}, /* 0xEA (11101010) */ + {1, 2, 4, 6, 7, 8, 0, 0}, /* 0xEB (11101011) */ + {3, 4, 6, 7, 8, 0, 0, 0}, /* 0xEC (11101100) */ + {1, 3, 4, 6, 7, 8, 0, 0}, /* 0xED (11101101) */ + {2, 3, 4, 6, 7, 8, 0, 0}, /* 0xEE (11101110) */ + {1, 2, 3, 4, 6, 7, 8, 0}, /* 0xEF (11101111) */ + {5, 6, 7, 8, 0, 0, 0, 0}, /* 0xF0 (11110000) */ + {1, 5, 6, 7, 8, 0, 0, 0}, /* 0xF1 (11110001) */ + {2, 5, 6, 7, 8, 0, 0, 0}, /* 0xF2 (11110010) */ + {1, 2, 5, 6, 7, 8, 0, 0}, /* 0xF3 (11110011) */ + {3, 5, 6, 7, 8, 0, 0, 0}, /* 0xF4 (11110100) */ + {1, 3, 5, 6, 7, 8, 0, 0}, /* 0xF5 (11110101) */ + {2, 3, 5, 6, 7, 8, 0, 0}, /* 0xF6 (11110110) */ + {1, 2, 3, 5, 6, 7, 8, 0}, /* 0xF7 (11110111) */ + {4, 5, 6, 7, 8, 0, 0, 0}, /* 0xF8 (11111000) */ + {1, 4, 5, 6, 7, 8, 0, 0}, /* 0xF9 (11111001) */ + {2, 4, 5, 6, 7, 8, 0, 0}, /* 0xFA (11111010) */ + {1, 2, 4, 5, 6, 7, 8, 0}, /* 0xFB (11111011) */ + {3, 4, 5, 6, 7, 8, 0, 0}, /* 0xFC (11111100) */ + {1, 3, 4, 5, 6, 7, 8, 0}, /* 0xFD (11111101) */ + {2, 3, 4, 5, 6, 7, 8, 0}, /* 0xFE (11111110) */ + {1, 2, 3, 4, 5, 6, 7, 8} /* 0xFF (11111111) */ +}; + +#endif + +#ifdef USEAVX + +size_t bitset_extract_setbits_avx2(uint64_t *array, size_t length, void *vout, + size_t outcapacity, uint32_t base) { + uint32_t *out = (uint32_t *)vout; + uint32_t *initout = out; + __m256i baseVec = _mm256_set1_epi32(base - 1); + __m256i incVec = _mm256_set1_epi32(64); + __m256i add8 = _mm256_set1_epi32(8); + uint32_t *safeout = out + outcapacity; + size_t i = 0; + for (; (i < length) && (out + 64 <= safeout); ++i) { + uint64_t w = array[i]; + if (w == 0) { + baseVec = _mm256_add_epi32(baseVec, incVec); + } else { + for (int k = 0; k < 4; ++k) { + uint8_t byteA = (uint8_t)w; + uint8_t byteB = (uint8_t)(w >> 8); + w >>= 16; + __m256i vecA = + _mm256_load_si256((const __m256i *)vecDecodeTable[byteA]); + __m256i vecB = + _mm256_load_si256((const __m256i *)vecDecodeTable[byteB]); + uint8_t advanceA = lengthTable[byteA]; + uint8_t advanceB = lengthTable[byteB]; + vecA = _mm256_add_epi32(baseVec, vecA); + baseVec = _mm256_add_epi32(baseVec, add8); + vecB = _mm256_add_epi32(baseVec, vecB); + baseVec = _mm256_add_epi32(baseVec, add8); + _mm256_storeu_si256((__m256i *)out, vecA); + out += advanceA; + _mm256_storeu_si256((__m256i *)out, vecB); + out += advanceB; + } + } + } + base += i * 64; + for (; (i < length) && (out < safeout); ++i) { + uint64_t w = array[i]; + while ((w != 0) && (out < safeout)) { + uint64_t t = w & (~w + 1); // on x64, should compile to BLSI (careful: the Intel compiler seems to fail) + int r = __builtin_ctzll(w); // on x64, should compile to TZCNT + uint32_t val = r + base; + memcpy(out, &val, + sizeof(uint32_t)); // should be compiled as a MOV on x64 + out++; + w ^= t; + } + base += 64; + } + return out - initout; +} +#endif // USEAVX + +size_t bitset_extract_setbits(uint64_t *bitset, size_t length, void *vout, + uint32_t base) { + int outpos = 0; + uint32_t *out = (uint32_t *)vout; + for (size_t i = 0; i < length; ++i) { + uint64_t w = bitset[i]; + while (w != 0) { + uint64_t t = w & (~w + 1); // on x64, should compile to BLSI (careful: the Intel compiler seems to fail) + int r = __builtin_ctzll(w); // on x64, should compile to TZCNT + uint32_t val = r + base; + memcpy(out + outpos, &val, + sizeof(uint32_t)); // should be compiled as a MOV on x64 + outpos++; + w ^= t; + } + base += 64; + } + return outpos; +} + +size_t bitset_extract_intersection_setbits_uint16(const uint64_t * __restrict__ bitset1, + const uint64_t * __restrict__ bitset2, + size_t length, uint16_t *out, + uint16_t base) { + int outpos = 0; + for (size_t i = 0; i < length; ++i) { + uint64_t w = bitset1[i] & bitset2[i]; + while (w != 0) { + uint64_t t = w & (~w + 1); + int r = __builtin_ctzll(w); + out[outpos++] = r + base; + w ^= t; + } + base += 64; + } + return outpos; +} + +#ifdef IS_X64 +/* + * Given a bitset containing "length" 64-bit words, write out the position + * of all the set bits to "out" as 16-bit integers, values start at "base" (can + *be set to zero). + * + * The "out" pointer should be sufficient to store the actual number of bits + *set. + * + * Returns how many values were actually decoded. + * + * This function uses SSE decoding. + */ +size_t bitset_extract_setbits_sse_uint16(const uint64_t *bitset, size_t length, + uint16_t *out, size_t outcapacity, + uint16_t base) { + uint16_t *initout = out; + __m128i baseVec = _mm_set1_epi16(base - 1); + __m128i incVec = _mm_set1_epi16(64); + __m128i add8 = _mm_set1_epi16(8); + uint16_t *safeout = out + outcapacity; + const int numberofbytes = 2; // process two bytes at a time + size_t i = 0; + for (; (i < length) && (out + numberofbytes * 8 <= safeout); ++i) { + uint64_t w = bitset[i]; + if (w == 0) { + baseVec = _mm_add_epi16(baseVec, incVec); + } else { + for (int k = 0; k < 4; ++k) { + uint8_t byteA = (uint8_t)w; + uint8_t byteB = (uint8_t)(w >> 8); + w >>= 16; + __m128i vecA = _mm_load_si128( + (const __m128i *)vecDecodeTable_uint16[byteA]); + __m128i vecB = _mm_load_si128( + (const __m128i *)vecDecodeTable_uint16[byteB]); + uint8_t advanceA = lengthTable[byteA]; + uint8_t advanceB = lengthTable[byteB]; + vecA = _mm_add_epi16(baseVec, vecA); + baseVec = _mm_add_epi16(baseVec, add8); + vecB = _mm_add_epi16(baseVec, vecB); + baseVec = _mm_add_epi16(baseVec, add8); + _mm_storeu_si128((__m128i *)out, vecA); + out += advanceA; + _mm_storeu_si128((__m128i *)out, vecB); + out += advanceB; + } + } + } + base += (uint16_t)(i * 64); + for (; (i < length) && (out < safeout); ++i) { + uint64_t w = bitset[i]; + while ((w != 0) && (out < safeout)) { + uint64_t t = w & (~w + 1); + int r = __builtin_ctzll(w); + *out = r + base; + out++; + w ^= t; + } + base += 64; + } + return out - initout; +} +#endif + +/* + * Given a bitset containing "length" 64-bit words, write out the position + * of all the set bits to "out", values start at "base" (can be set to zero). + * + * The "out" pointer should be sufficient to store the actual number of bits + *set. + * + * Returns how many values were actually decoded. + */ +size_t bitset_extract_setbits_uint16(const uint64_t *bitset, size_t length, + uint16_t *out, uint16_t base) { + int outpos = 0; + for (size_t i = 0; i < length; ++i) { + uint64_t w = bitset[i]; + while (w != 0) { + uint64_t t = w & (~w + 1); + int r = __builtin_ctzll(w); + out[outpos++] = r + base; + w ^= t; + } + base += 64; + } + return outpos; +} + +#if defined(ASMBITMANIPOPTIMIZATION) + +uint64_t bitset_set_list_withcard(void *bitset, uint64_t card, + const uint16_t *list, uint64_t length) { + uint64_t offset, load, pos; + uint64_t shift = 6; + const uint16_t *end = list + length; + if (!length) return card; + // TODO: could unroll for performance, see bitset_set_list + // bts is not available as an intrinsic in GCC + __asm volatile( + "1:\n" + "movzwq (%[list]), %[pos]\n" + "shrx %[shift], %[pos], %[offset]\n" + "mov (%[bitset],%[offset],8), %[load]\n" + "bts %[pos], %[load]\n" + "mov %[load], (%[bitset],%[offset],8)\n" + "sbb $-1, %[card]\n" + "add $2, %[list]\n" + "cmp %[list], %[end]\n" + "jnz 1b" + : [card] "+&r"(card), [list] "+&r"(list), [load] "=&r"(load), + [pos] "=&r"(pos), [offset] "=&r"(offset) + : [end] "r"(end), [bitset] "r"(bitset), [shift] "r"(shift)); + return card; +} + +void bitset_set_list(void *bitset, const uint16_t *list, uint64_t length) { + uint64_t pos; + const uint16_t *end = list + length; + + uint64_t shift = 6; + uint64_t offset; + uint64_t load; + for (; list + 3 < end; list += 4) { + pos = list[0]; + __asm volatile( + "shrx %[shift], %[pos], %[offset]\n" + "mov (%[bitset],%[offset],8), %[load]\n" + "bts %[pos], %[load]\n" + "mov %[load], (%[bitset],%[offset],8)" + : [load] "=&r"(load), [offset] "=&r"(offset) + : [bitset] "r"(bitset), [shift] "r"(shift), [pos] "r"(pos)); + pos = list[1]; + __asm volatile( + "shrx %[shift], %[pos], %[offset]\n" + "mov (%[bitset],%[offset],8), %[load]\n" + "bts %[pos], %[load]\n" + "mov %[load], (%[bitset],%[offset],8)" + : [load] "=&r"(load), [offset] "=&r"(offset) + : [bitset] "r"(bitset), [shift] "r"(shift), [pos] "r"(pos)); + pos = list[2]; + __asm volatile( + "shrx %[shift], %[pos], %[offset]\n" + "mov (%[bitset],%[offset],8), %[load]\n" + "bts %[pos], %[load]\n" + "mov %[load], (%[bitset],%[offset],8)" + : [load] "=&r"(load), [offset] "=&r"(offset) + : [bitset] "r"(bitset), [shift] "r"(shift), [pos] "r"(pos)); + pos = list[3]; + __asm volatile( + "shrx %[shift], %[pos], %[offset]\n" + "mov (%[bitset],%[offset],8), %[load]\n" + "bts %[pos], %[load]\n" + "mov %[load], (%[bitset],%[offset],8)" + : [load] "=&r"(load), [offset] "=&r"(offset) + : [bitset] "r"(bitset), [shift] "r"(shift), [pos] "r"(pos)); + } + + while (list != end) { + pos = list[0]; + __asm volatile( + "shrx %[shift], %[pos], %[offset]\n" + "mov (%[bitset],%[offset],8), %[load]\n" + "bts %[pos], %[load]\n" + "mov %[load], (%[bitset],%[offset],8)" + : [load] "=&r"(load), [offset] "=&r"(offset) + : [bitset] "r"(bitset), [shift] "r"(shift), [pos] "r"(pos)); + list++; + } +} + +uint64_t bitset_clear_list(void *bitset, uint64_t card, const uint16_t *list, + uint64_t length) { + uint64_t offset, load, pos; + uint64_t shift = 6; + const uint16_t *end = list + length; + if (!length) return card; + // btr is not available as an intrinsic in GCC + __asm volatile( + "1:\n" + "movzwq (%[list]), %[pos]\n" + "shrx %[shift], %[pos], %[offset]\n" + "mov (%[bitset],%[offset],8), %[load]\n" + "btr %[pos], %[load]\n" + "mov %[load], (%[bitset],%[offset],8)\n" + "sbb $0, %[card]\n" + "add $2, %[list]\n" + "cmp %[list], %[end]\n" + "jnz 1b" + : [card] "+&r"(card), [list] "+&r"(list), [load] "=&r"(load), + [pos] "=&r"(pos), [offset] "=&r"(offset) + : [end] "r"(end), [bitset] "r"(bitset), [shift] "r"(shift) + : + /* clobbers */ "memory"); + return card; +} + +#else +uint64_t bitset_clear_list(void *bitset, uint64_t card, const uint16_t *list, + uint64_t length) { + uint64_t offset, load, newload, pos, index; + const uint16_t *end = list + length; + while (list != end) { + pos = *(const uint16_t *)list; + offset = pos >> 6; + index = pos % 64; + load = ((uint64_t *)bitset)[offset]; + newload = load & ~(UINT64_C(1) << index); + card -= (load ^ newload) >> index; + ((uint64_t *)bitset)[offset] = newload; + list++; + } + return card; +} + +uint64_t bitset_set_list_withcard(void *bitset, uint64_t card, + const uint16_t *list, uint64_t length) { + uint64_t offset, load, newload, pos, index; + const uint16_t *end = list + length; + while (list != end) { + pos = *(const uint16_t *)list; + offset = pos >> 6; + index = pos % 64; + load = ((uint64_t *)bitset)[offset]; + newload = load | (UINT64_C(1) << index); + card += (load ^ newload) >> index; + ((uint64_t *)bitset)[offset] = newload; + list++; + } + return card; +} + +void bitset_set_list(void *bitset, const uint16_t *list, uint64_t length) { + uint64_t offset, load, newload, pos, index; + const uint16_t *end = list + length; + while (list != end) { + pos = *(const uint16_t *)list; + offset = pos >> 6; + index = pos % 64; + load = ((uint64_t *)bitset)[offset]; + newload = load | (UINT64_C(1) << index); + ((uint64_t *)bitset)[offset] = newload; + list++; + } +} + +#endif + +/* flip specified bits */ +/* TODO: consider whether worthwhile to make an asm version */ + +uint64_t bitset_flip_list_withcard(void *bitset, uint64_t card, + const uint16_t *list, uint64_t length) { + uint64_t offset, load, newload, pos, index; + const uint16_t *end = list + length; + while (list != end) { + pos = *(const uint16_t *)list; + offset = pos >> 6; + index = pos % 64; + load = ((uint64_t *)bitset)[offset]; + newload = load ^ (UINT64_C(1) << index); + // todo: is a branch here all that bad? + card += + (1 - 2 * (((UINT64_C(1) << index) & load) >> index)); // +1 or -1 + ((uint64_t *)bitset)[offset] = newload; + list++; + } + return card; +} + +void bitset_flip_list(void *bitset, const uint16_t *list, uint64_t length) { + uint64_t offset, load, newload, pos, index; + const uint16_t *end = list + length; + while (list != end) { + pos = *(const uint16_t *)list; + offset = pos >> 6; + index = pos % 64; + load = ((uint64_t *)bitset)[offset]; + newload = load ^ (UINT64_C(1) << index); + ((uint64_t *)bitset)[offset] = newload; + list++; + } +} +/* end file src/bitset_util.c */ +/* begin file src/containers/array.c */ +/* + * array.c + * + */ + +#include +#include +#include + +/* Create a new array with capacity size. Return NULL in case of failure. */ +array_container_t *array_container_create_given_capacity(int32_t size) { + array_container_t *container; + + container = (array_container_t *)malloc(sizeof(array_container_t)); + assert (container); + + if( size <= 0 ) { // we don't want to rely on malloc(0) + container->array = NULL; + } else { + container->array = (uint16_t *)malloc(sizeof(uint16_t) * size); + assert (container->array); + } + + container->capacity = size; + container->cardinality = 0; + + return container; +} + +/* Create a new array. Return NULL in case of failure. */ +array_container_t *array_container_create(void) { + return array_container_create_given_capacity(ARRAY_DEFAULT_INIT_SIZE); +} + +/* Create a new array containing all values in [min,max). */ +array_container_t * array_container_create_range(uint32_t min, uint32_t max) { + array_container_t * answer = array_container_create_given_capacity(max - min + 1); + if(answer == NULL) return answer; + answer->cardinality = 0; + for(uint32_t k = min; k < max; k++) { + answer->array[answer->cardinality++] = k; + } + return answer; +} + +/* Duplicate container */ +array_container_t *array_container_clone(const array_container_t *src) { + array_container_t *newcontainer = + array_container_create_given_capacity(src->capacity); + if (newcontainer == NULL) return NULL; + + newcontainer->cardinality = src->cardinality; + + memcpy(newcontainer->array, src->array, + src->cardinality * sizeof(uint16_t)); + + return newcontainer; +} + +int array_container_shrink_to_fit(array_container_t *src) { + if (src->cardinality == src->capacity) return 0; // nothing to do + int savings = src->capacity - src->cardinality; + src->capacity = src->cardinality; + if( src->capacity == 0) { // we do not want to rely on realloc for zero allocs + free(src->array); + src->array = NULL; + } else { + uint16_t *oldarray = src->array; + src->array = + (uint16_t *)realloc(oldarray, src->capacity * sizeof(uint16_t)); + } + return savings; +} + +/* Free memory. */ +void array_container_free(array_container_t *arr) { + if(arr->array != NULL) {// Jon Strabala reports that some tools complain otherwise + free(arr->array); + arr->array = NULL; // pedantic + } + free(arr); +} + +static inline int32_t grow_capacity(int32_t capacity) { + return (capacity <= 0) ? ARRAY_DEFAULT_INIT_SIZE + : capacity < 64 ? capacity * 2 + : capacity < 1024 ? capacity * 3 / 2 + : capacity * 5 / 4; +} + +static inline int32_t clamp(int32_t val, int32_t min, int32_t max) { + return ((val < min) ? min : (val > max) ? max : val); +} + +void array_container_grow(array_container_t *container, int32_t min, + bool preserve) { + + int32_t max = (min <= DEFAULT_MAX_SIZE ? DEFAULT_MAX_SIZE : 65536); + int32_t new_capacity = clamp(grow_capacity(container->capacity), min, max); + + container->capacity = new_capacity; + uint16_t *array = container->array; + + if (preserve) { + container->array = + (uint16_t *)realloc(array, new_capacity * sizeof(uint16_t)); + } else { + // Jon Strabala reports that some tools complain otherwise + if (array != NULL) { + free(array); + } + container->array = (uint16_t *)malloc(new_capacity * sizeof(uint16_t)); + } + + // handle the case where realloc fails + if (container->array == NULL) { + fprintf(stderr, "could not allocate memory\n"); + } + assert(container->array != NULL); +} + +/* Copy one container into another. We assume that they are distinct. */ +void array_container_copy(const array_container_t *src, + array_container_t *dst) { + const int32_t cardinality = src->cardinality; + if (cardinality > dst->capacity) { + array_container_grow(dst, cardinality, false); + } + + dst->cardinality = cardinality; + memcpy(dst->array, src->array, cardinality * sizeof(uint16_t)); +} + +void array_container_add_from_range(array_container_t *arr, uint32_t min, + uint32_t max, uint16_t step) { + for (uint32_t value = min; value < max; value += step) { + array_container_append(arr, value); + } +} + +/* Computes the union of array1 and array2 and write the result to arrayout. + * It is assumed that arrayout is distinct from both array1 and array2. + */ +void array_container_union(const array_container_t *array_1, + const array_container_t *array_2, + array_container_t *out) { + const int32_t card_1 = array_1->cardinality, card_2 = array_2->cardinality; + const int32_t max_cardinality = card_1 + card_2; + + if (out->capacity < max_cardinality) { + array_container_grow(out, max_cardinality, false); + } + out->cardinality = (int32_t)fast_union_uint16(array_1->array, card_1, + array_2->array, card_2, out->array); + +} + +/* Computes the difference of array1 and array2 and write the result + * to array out. + * Array out does not need to be distinct from array_1 + */ +void array_container_andnot(const array_container_t *array_1, + const array_container_t *array_2, + array_container_t *out) { + if (out->capacity < array_1->cardinality) + array_container_grow(out, array_1->cardinality, false); +#ifdef ROARING_VECTOR_OPERATIONS_ENABLED + if((out != array_1) && (out != array_2)) { + out->cardinality = + difference_vector16(array_1->array, array_1->cardinality, + array_2->array, array_2->cardinality, out->array); + } else { + out->cardinality = + difference_uint16(array_1->array, array_1->cardinality, array_2->array, + array_2->cardinality, out->array); + } +#else + out->cardinality = + difference_uint16(array_1->array, array_1->cardinality, array_2->array, + array_2->cardinality, out->array); +#endif +} + +/* Computes the symmetric difference of array1 and array2 and write the + * result + * to arrayout. + * It is assumed that arrayout is distinct from both array1 and array2. + */ +void array_container_xor(const array_container_t *array_1, + const array_container_t *array_2, + array_container_t *out) { + const int32_t card_1 = array_1->cardinality, card_2 = array_2->cardinality; + const int32_t max_cardinality = card_1 + card_2; + if (out->capacity < max_cardinality) { + array_container_grow(out, max_cardinality, false); + } + +#ifdef ROARING_VECTOR_OPERATIONS_ENABLED + out->cardinality = + xor_vector16(array_1->array, array_1->cardinality, array_2->array, + array_2->cardinality, out->array); +#else + out->cardinality = + xor_uint16(array_1->array, array_1->cardinality, array_2->array, + array_2->cardinality, out->array); +#endif +} + +static inline int32_t minimum_int32(int32_t a, int32_t b) { + return (a < b) ? a : b; +} + +/* computes the intersection of array1 and array2 and write the result to + * arrayout. + * It is assumed that arrayout is distinct from both array1 and array2. + * */ +void array_container_intersection(const array_container_t *array1, + const array_container_t *array2, + array_container_t *out) { + int32_t card_1 = array1->cardinality, card_2 = array2->cardinality, + min_card = minimum_int32(card_1, card_2); + const int threshold = 64; // subject to tuning +#ifdef USEAVX + if (out->capacity < min_card) { + array_container_grow(out, min_card + sizeof(__m128i) / sizeof(uint16_t), + false); + } +#else + if (out->capacity < min_card) { + array_container_grow(out, min_card, false); + } +#endif + + if (card_1 * threshold < card_2) { + out->cardinality = intersect_skewed_uint16( + array1->array, card_1, array2->array, card_2, out->array); + } else if (card_2 * threshold < card_1) { + out->cardinality = intersect_skewed_uint16( + array2->array, card_2, array1->array, card_1, out->array); + } else { +#ifdef USEAVX + out->cardinality = intersect_vector16( + array1->array, card_1, array2->array, card_2, out->array); +#else + out->cardinality = intersect_uint16(array1->array, card_1, + array2->array, card_2, out->array); +#endif + } +} + +/* computes the size of the intersection of array1 and array2 + * */ +int array_container_intersection_cardinality(const array_container_t *array1, + const array_container_t *array2) { + int32_t card_1 = array1->cardinality, card_2 = array2->cardinality; + const int threshold = 64; // subject to tuning + if (card_1 * threshold < card_2) { + return intersect_skewed_uint16_cardinality(array1->array, card_1, + array2->array, card_2); + } else if (card_2 * threshold < card_1) { + return intersect_skewed_uint16_cardinality(array2->array, card_2, + array1->array, card_1); + } else { +#ifdef USEAVX + return intersect_vector16_cardinality(array1->array, card_1, + array2->array, card_2); +#else + return intersect_uint16_cardinality(array1->array, card_1, + array2->array, card_2); +#endif + } +} + +bool array_container_intersect(const array_container_t *array1, + const array_container_t *array2) { + int32_t card_1 = array1->cardinality, card_2 = array2->cardinality; + const int threshold = 64; // subject to tuning + if (card_1 * threshold < card_2) { + return intersect_skewed_uint16_nonempty( + array1->array, card_1, array2->array, card_2); + } else if (card_2 * threshold < card_1) { + return intersect_skewed_uint16_nonempty( + array2->array, card_2, array1->array, card_1); + } else { + // we do not bother vectorizing + return intersect_uint16_nonempty(array1->array, card_1, + array2->array, card_2); + } +} + +/* computes the intersection of array1 and array2 and write the result to + * array1. + * */ +void array_container_intersection_inplace(array_container_t *src_1, + const array_container_t *src_2) { + // todo: can any of this be vectorized? + int32_t card_1 = src_1->cardinality, card_2 = src_2->cardinality; + const int threshold = 64; // subject to tuning + if (card_1 * threshold < card_2) { + src_1->cardinality = intersect_skewed_uint16( + src_1->array, card_1, src_2->array, card_2, src_1->array); + } else if (card_2 * threshold < card_1) { + src_1->cardinality = intersect_skewed_uint16( + src_2->array, card_2, src_1->array, card_1, src_1->array); + } else { + src_1->cardinality = intersect_uint16( + src_1->array, card_1, src_2->array, card_2, src_1->array); + } +} + +int array_container_to_uint32_array(void *vout, const array_container_t *cont, + uint32_t base) { + int outpos = 0; + uint32_t *out = (uint32_t *)vout; + for (int i = 0; i < cont->cardinality; ++i) { + const uint32_t val = base + cont->array[i]; + memcpy(out + outpos, &val, + sizeof(uint32_t)); // should be compiled as a MOV on x64 + outpos++; + } + return outpos; +} + +void array_container_printf(const array_container_t *v) { + if (v->cardinality == 0) { + printf("{}"); + return; + } + printf("{"); + printf("%d", v->array[0]); + for (int i = 1; i < v->cardinality; ++i) { + printf(",%d", v->array[i]); + } + printf("}"); +} + +void array_container_printf_as_uint32_array(const array_container_t *v, + uint32_t base) { + if (v->cardinality == 0) { + return; + } + printf("%u", v->array[0] + base); + for (int i = 1; i < v->cardinality; ++i) { + printf(",%u", v->array[i] + base); + } +} + +/* Compute the number of runs */ +int32_t array_container_number_of_runs(const array_container_t *a) { + // Can SIMD work here? + int32_t nr_runs = 0; + int32_t prev = -2; + for (const uint16_t *p = a->array; p != a->array + a->cardinality; ++p) { + if (*p != prev + 1) nr_runs++; + prev = *p; + } + return nr_runs; +} + +int32_t array_container_serialize(const array_container_t *container, char *buf) { + int32_t l, off; + uint16_t cardinality = (uint16_t)container->cardinality; + + memcpy(buf, &cardinality, off = sizeof(cardinality)); + l = sizeof(uint16_t) * container->cardinality; + if (l) memcpy(&buf[off], container->array, l); + + return (off + l); +} + +/** + * Writes the underlying array to buf, outputs how many bytes were written. + * The number of bytes written should be + * array_container_size_in_bytes(container). + * + */ +int32_t array_container_write(const array_container_t *container, char *buf) { + memcpy(buf, container->array, container->cardinality * sizeof(uint16_t)); + return array_container_size_in_bytes(container); +} + +bool array_container_is_subset(const array_container_t *container1, + const array_container_t *container2) { + if (container1->cardinality > container2->cardinality) { + return false; + } + int i1 = 0, i2 = 0; + while (i1 < container1->cardinality && i2 < container2->cardinality) { + if (container1->array[i1] == container2->array[i2]) { + i1++; + i2++; + } else if (container1->array[i1] > container2->array[i2]) { + i2++; + } else { // container1->array[i1] < container2->array[i2] + return false; + } + } + if (i1 == container1->cardinality) { + return true; + } else { + return false; + } +} + +int32_t array_container_read(int32_t cardinality, array_container_t *container, + const char *buf) { + if (container->capacity < cardinality) { + array_container_grow(container, cardinality, false); + } + container->cardinality = cardinality; + memcpy(container->array, buf, container->cardinality * sizeof(uint16_t)); + + return array_container_size_in_bytes(container); +} + +uint32_t array_container_serialization_len(const array_container_t *container) { + return (sizeof(uint16_t) /* container->cardinality converted to 16 bit */ + + (sizeof(uint16_t) * container->cardinality)); +} + +void *array_container_deserialize(const char *buf, size_t buf_len) { + array_container_t *ptr; + + if (buf_len < 2) /* capacity converted to 16 bit */ + return (NULL); + else + buf_len -= 2; + + if ((ptr = (array_container_t *)malloc(sizeof(array_container_t))) != + NULL) { + size_t len; + int32_t off; + uint16_t cardinality; + + memcpy(&cardinality, buf, off = sizeof(cardinality)); + + ptr->capacity = ptr->cardinality = (uint32_t)cardinality; + len = sizeof(uint16_t) * ptr->cardinality; + + if (len != buf_len) { + free(ptr); + return (NULL); + } + + if ((ptr->array = (uint16_t *)malloc(sizeof(uint16_t) * + ptr->capacity)) == NULL) { + free(ptr); + return (NULL); + } + + if (len) memcpy(ptr->array, &buf[off], len); + + /* Check if returned values are monotonically increasing */ + for (int32_t i = 0, j = 0; i < ptr->cardinality; i++) { + if (ptr->array[i] < j) { + free(ptr->array); + free(ptr); + return (NULL); + } else + j = ptr->array[i]; + } + } + + return (ptr); +} + +bool array_container_iterate(const array_container_t *cont, uint32_t base, + roaring_iterator iterator, void *ptr) { + for (int i = 0; i < cont->cardinality; i++) + if (!iterator(cont->array[i] + base, ptr)) return false; + return true; +} + +bool array_container_iterate64(const array_container_t *cont, uint32_t base, + roaring_iterator64 iterator, uint64_t high_bits, + void *ptr) { + for (int i = 0; i < cont->cardinality; i++) + if (!iterator(high_bits | (uint64_t)(cont->array[i] + base), ptr)) + return false; + return true; +} +/* end file src/containers/array.c */ +/* begin file src/containers/bitset.c */ +/* + * bitset.c + * + */ +#ifndef _POSIX_C_SOURCE +#define _POSIX_C_SOURCE 200809L +#endif +#include +#include +#include +#include + + +void bitset_container_clear(bitset_container_t *bitset) { + memset(bitset->array, 0, sizeof(uint64_t) * BITSET_CONTAINER_SIZE_IN_WORDS); + bitset->cardinality = 0; +} + +void bitset_container_set_all(bitset_container_t *bitset) { + memset(bitset->array, INT64_C(-1), + sizeof(uint64_t) * BITSET_CONTAINER_SIZE_IN_WORDS); + bitset->cardinality = (1 << 16); +} + + + +/* Create a new bitset. Return NULL in case of failure. */ +bitset_container_t *bitset_container_create(void) { + bitset_container_t *bitset = + (bitset_container_t *)malloc(sizeof(bitset_container_t)); + + if (!bitset) { + return NULL; + } + // sizeof(__m256i) == 32 + bitset->array = (uint64_t *)roaring_bitmap_aligned_malloc( + 32, sizeof(uint64_t) * BITSET_CONTAINER_SIZE_IN_WORDS); + if (!bitset->array) { + free(bitset); + return NULL; + } + bitset_container_clear(bitset); + return bitset; +} + +/* Copy one container into another. We assume that they are distinct. */ +void bitset_container_copy(const bitset_container_t *source, + bitset_container_t *dest) { + dest->cardinality = source->cardinality; + memcpy(dest->array, source->array, + sizeof(uint64_t) * BITSET_CONTAINER_SIZE_IN_WORDS); +} + +void bitset_container_add_from_range(bitset_container_t *bitset, uint32_t min, + uint32_t max, uint16_t step) { + if (step == 0) return; // refuse to crash + if ((64 % step) == 0) { // step divides 64 + uint64_t mask = 0; // construct the repeated mask + for (uint32_t value = (min % step); value < 64; value += step) { + mask |= ((uint64_t)1 << value); + } + uint32_t firstword = min / 64; + uint32_t endword = (max - 1) / 64; + bitset->cardinality = (max - min + step - 1) / step; + if (firstword == endword) { + bitset->array[firstword] |= + mask & (((~UINT64_C(0)) << (min % 64)) & + ((~UINT64_C(0)) >> ((~max + 1) % 64))); + return; + } + bitset->array[firstword] = mask & ((~UINT64_C(0)) << (min % 64)); + for (uint32_t i = firstword + 1; i < endword; i++) + bitset->array[i] = mask; + bitset->array[endword] = mask & ((~UINT64_C(0)) >> ((~max + 1) % 64)); + } else { + for (uint32_t value = min; value < max; value += step) { + bitset_container_add(bitset, value); + } + } +} + +/* Free memory. */ +void bitset_container_free(bitset_container_t *bitset) { + if(bitset->array != NULL) {// Jon Strabala reports that some tools complain otherwise + roaring_bitmap_aligned_free(bitset->array); + bitset->array = NULL; // pedantic + } + free(bitset); +} + +/* duplicate container. */ +bitset_container_t *bitset_container_clone(const bitset_container_t *src) { + bitset_container_t *bitset = + (bitset_container_t *)malloc(sizeof(bitset_container_t)); + assert(bitset); + + // sizeof(__m256i) == 32 + bitset->array = (uint64_t *)roaring_bitmap_aligned_malloc( + 32, sizeof(uint64_t) * BITSET_CONTAINER_SIZE_IN_WORDS); + assert(bitset->array); + bitset->cardinality = src->cardinality; + memcpy(bitset->array, src->array, + sizeof(uint64_t) * BITSET_CONTAINER_SIZE_IN_WORDS); + return bitset; +} + +void bitset_container_set_range(bitset_container_t *bitset, uint32_t begin, + uint32_t end) { + bitset_set_range(bitset->array, begin, end); + bitset->cardinality = + bitset_container_compute_cardinality(bitset); // could be smarter +} + + +bool bitset_container_intersect(const bitset_container_t *src_1, + const bitset_container_t *src_2) { + // could vectorize, but this is probably already quite fast in practice + const uint64_t * __restrict__ array_1 = src_1->array; + const uint64_t * __restrict__ array_2 = src_2->array; + for (int i = 0; i < BITSET_CONTAINER_SIZE_IN_WORDS; i ++) { + if((array_1[i] & array_2[i]) != 0) return true; + } + return false; +} + + +#ifdef USEAVX +#ifndef WORDS_IN_AVX2_REG +#define WORDS_IN_AVX2_REG sizeof(__m256i) / sizeof(uint64_t) +#endif +/* Get the number of bits set (force computation) */ +int bitset_container_compute_cardinality(const bitset_container_t *bitset) { + return (int) avx2_harley_seal_popcount256( + (const __m256i *)bitset->array, + BITSET_CONTAINER_SIZE_IN_WORDS / (WORDS_IN_AVX2_REG)); +} + +#elif defined(USENEON) +int bitset_container_compute_cardinality(const bitset_container_t *bitset) { + uint16x8_t n0 = vdupq_n_u16(0); + uint16x8_t n1 = vdupq_n_u16(0); + uint16x8_t n2 = vdupq_n_u16(0); + uint16x8_t n3 = vdupq_n_u16(0); + for (size_t i = 0; i < BITSET_CONTAINER_SIZE_IN_WORDS; i += 8) { + uint64x2_t c0 = vld1q_u64(&bitset->array[i + 0]); + n0 = vaddq_u16(n0, vpaddlq_u8(vcntq_u8(vreinterpretq_u8_u64(c0)))); + uint64x2_t c1 = vld1q_u64(&bitset->array[i + 2]); + n1 = vaddq_u16(n1, vpaddlq_u8(vcntq_u8(vreinterpretq_u8_u64(c1)))); + uint64x2_t c2 = vld1q_u64(&bitset->array[i + 4]); + n2 = vaddq_u16(n2, vpaddlq_u8(vcntq_u8(vreinterpretq_u8_u64(c2)))); + uint64x2_t c3 = vld1q_u64(&bitset->array[i + 6]); + n3 = vaddq_u16(n3, vpaddlq_u8(vcntq_u8(vreinterpretq_u8_u64(c3)))); + } + uint64x2_t n = vdupq_n_u64(0); + n = vaddq_u64(n, vpaddlq_u32(vpaddlq_u16(n0))); + n = vaddq_u64(n, vpaddlq_u32(vpaddlq_u16(n1))); + n = vaddq_u64(n, vpaddlq_u32(vpaddlq_u16(n2))); + n = vaddq_u64(n, vpaddlq_u32(vpaddlq_u16(n3))); + return vgetq_lane_u64(n, 0) + vgetq_lane_u64(n, 1); +} + +#else + +/* Get the number of bits set (force computation) */ +int bitset_container_compute_cardinality(const bitset_container_t *bitset) { + const uint64_t *array = bitset->array; + int32_t sum = 0; + for (int i = 0; i < BITSET_CONTAINER_SIZE_IN_WORDS; i += 4) { + sum += hamming(array[i]); + sum += hamming(array[i + 1]); + sum += hamming(array[i + 2]); + sum += hamming(array[i + 3]); + } + return sum; +} + +#endif + +#ifdef USEAVX + +#define BITSET_CONTAINER_FN_REPEAT 8 +#ifndef WORDS_IN_AVX2_REG +#define WORDS_IN_AVX2_REG sizeof(__m256i) / sizeof(uint64_t) +#endif +#define LOOP_SIZE \ + BITSET_CONTAINER_SIZE_IN_WORDS / \ + ((WORDS_IN_AVX2_REG)*BITSET_CONTAINER_FN_REPEAT) + +/* Computes a binary operation (eg union) on bitset1 and bitset2 and write the + result to bitsetout */ +// clang-format off +#define BITSET_CONTAINER_FN(opname, opsymbol, avx_intrinsic, neon_intrinsic) \ +int bitset_container_##opname##_nocard(const bitset_container_t *src_1, \ + const bitset_container_t *src_2, \ + bitset_container_t *dst) { \ + const uint8_t * __restrict__ array_1 = (const uint8_t *)src_1->array; \ + const uint8_t * __restrict__ array_2 = (const uint8_t *)src_2->array; \ + /* not using the blocking optimization for some reason*/ \ + uint8_t *out = (uint8_t*)dst->array; \ + const int innerloop = 8; \ + for (size_t i = 0; \ + i < BITSET_CONTAINER_SIZE_IN_WORDS / (WORDS_IN_AVX2_REG); \ + i+=innerloop) {\ + __m256i A1, A2, AO; \ + A1 = _mm256_lddqu_si256((const __m256i *)(array_1)); \ + A2 = _mm256_lddqu_si256((const __m256i *)(array_2)); \ + AO = avx_intrinsic(A2, A1); \ + _mm256_storeu_si256((__m256i *)out, AO); \ + A1 = _mm256_lddqu_si256((const __m256i *)(array_1 + 32)); \ + A2 = _mm256_lddqu_si256((const __m256i *)(array_2 + 32)); \ + AO = avx_intrinsic(A2, A1); \ + _mm256_storeu_si256((__m256i *)(out+32), AO); \ + A1 = _mm256_lddqu_si256((const __m256i *)(array_1 + 64)); \ + A2 = _mm256_lddqu_si256((const __m256i *)(array_2 + 64)); \ + AO = avx_intrinsic(A2, A1); \ + _mm256_storeu_si256((__m256i *)(out+64), AO); \ + A1 = _mm256_lddqu_si256((const __m256i *)(array_1 + 96)); \ + A2 = _mm256_lddqu_si256((const __m256i *)(array_2 + 96)); \ + AO = avx_intrinsic(A2, A1); \ + _mm256_storeu_si256((__m256i *)(out+96), AO); \ + A1 = _mm256_lddqu_si256((const __m256i *)(array_1 + 128)); \ + A2 = _mm256_lddqu_si256((const __m256i *)(array_2 + 128)); \ + AO = avx_intrinsic(A2, A1); \ + _mm256_storeu_si256((__m256i *)(out+128), AO); \ + A1 = _mm256_lddqu_si256((const __m256i *)(array_1 + 160)); \ + A2 = _mm256_lddqu_si256((const __m256i *)(array_2 + 160)); \ + AO = avx_intrinsic(A2, A1); \ + _mm256_storeu_si256((__m256i *)(out+160), AO); \ + A1 = _mm256_lddqu_si256((const __m256i *)(array_1 + 192)); \ + A2 = _mm256_lddqu_si256((const __m256i *)(array_2 + 192)); \ + AO = avx_intrinsic(A2, A1); \ + _mm256_storeu_si256((__m256i *)(out+192), AO); \ + A1 = _mm256_lddqu_si256((const __m256i *)(array_1 + 224)); \ + A2 = _mm256_lddqu_si256((const __m256i *)(array_2 + 224)); \ + AO = avx_intrinsic(A2, A1); \ + _mm256_storeu_si256((__m256i *)(out+224), AO); \ + out+=256; \ + array_1 += 256; \ + array_2 += 256; \ + } \ + dst->cardinality = BITSET_UNKNOWN_CARDINALITY; \ + return dst->cardinality; \ +} \ +/* next, a version that updates cardinality*/ \ +int bitset_container_##opname(const bitset_container_t *src_1, \ + const bitset_container_t *src_2, \ + bitset_container_t *dst) { \ + const __m256i * __restrict__ array_1 = (const __m256i *) src_1->array; \ + const __m256i * __restrict__ array_2 = (const __m256i *) src_2->array; \ + __m256i *out = (__m256i *) dst->array; \ + dst->cardinality = (int32_t)avx2_harley_seal_popcount256andstore_##opname(array_2,\ + array_1, out,BITSET_CONTAINER_SIZE_IN_WORDS / (WORDS_IN_AVX2_REG));\ + return dst->cardinality; \ +} \ +/* next, a version that just computes the cardinality*/ \ +int bitset_container_##opname##_justcard(const bitset_container_t *src_1, \ + const bitset_container_t *src_2) { \ + const __m256i * __restrict__ data1 = (const __m256i *) src_1->array; \ + const __m256i * __restrict__ data2 = (const __m256i *) src_2->array; \ + return (int)avx2_harley_seal_popcount256_##opname(data2, \ + data1, BITSET_CONTAINER_SIZE_IN_WORDS / (WORDS_IN_AVX2_REG));\ +} + +#elif defined(USENEON) + +#define BITSET_CONTAINER_FN(opname, opsymbol, avx_intrinsic, neon_intrinsic) \ +int bitset_container_##opname(const bitset_container_t *src_1, \ + const bitset_container_t *src_2, \ + bitset_container_t *dst) { \ + const uint64_t * __restrict__ array_1 = src_1->array; \ + const uint64_t * __restrict__ array_2 = src_2->array; \ + uint64_t *out = dst->array; \ + uint16x8_t n0 = vdupq_n_u16(0); \ + uint16x8_t n1 = vdupq_n_u16(0); \ + uint16x8_t n2 = vdupq_n_u16(0); \ + uint16x8_t n3 = vdupq_n_u16(0); \ + for (size_t i = 0; i < BITSET_CONTAINER_SIZE_IN_WORDS; i += 8) { \ + uint64x2_t c0 = neon_intrinsic(vld1q_u64(&array_1[i + 0]), \ + vld1q_u64(&array_2[i + 0])); \ + n0 = vaddq_u16(n0, vpaddlq_u8(vcntq_u8(vreinterpretq_u8_u64(c0)))); \ + vst1q_u64(&out[i + 0], c0); \ + uint64x2_t c1 = neon_intrinsic(vld1q_u64(&array_1[i + 2]), \ + vld1q_u64(&array_2[i + 2])); \ + n1 = vaddq_u16(n1, vpaddlq_u8(vcntq_u8(vreinterpretq_u8_u64(c1)))); \ + vst1q_u64(&out[i + 2], c1); \ + uint64x2_t c2 = neon_intrinsic(vld1q_u64(&array_1[i + 4]), \ + vld1q_u64(&array_2[i + 4])); \ + n2 = vaddq_u16(n2, vpaddlq_u8(vcntq_u8(vreinterpretq_u8_u64(c2)))); \ + vst1q_u64(&out[i + 4], c2); \ + uint64x2_t c3 = neon_intrinsic(vld1q_u64(&array_1[i + 6]), \ + vld1q_u64(&array_2[i + 6])); \ + n3 = vaddq_u16(n3, vpaddlq_u8(vcntq_u8(vreinterpretq_u8_u64(c3)))); \ + vst1q_u64(&out[i + 6], c3); \ + } \ + uint64x2_t n = vdupq_n_u64(0); \ + n = vaddq_u64(n, vpaddlq_u32(vpaddlq_u16(n0))); \ + n = vaddq_u64(n, vpaddlq_u32(vpaddlq_u16(n1))); \ + n = vaddq_u64(n, vpaddlq_u32(vpaddlq_u16(n2))); \ + n = vaddq_u64(n, vpaddlq_u32(vpaddlq_u16(n3))); \ + dst->cardinality = vgetq_lane_u64(n, 0) + vgetq_lane_u64(n, 1); \ + return dst->cardinality; \ +} \ +int bitset_container_##opname##_nocard(const bitset_container_t *src_1, \ + const bitset_container_t *src_2, \ + bitset_container_t *dst) { \ + const uint64_t * __restrict__ array_1 = src_1->array; \ + const uint64_t * __restrict__ array_2 = src_2->array; \ + uint64_t *out = dst->array; \ + for (size_t i = 0; i < BITSET_CONTAINER_SIZE_IN_WORDS; i += 8) { \ + vst1q_u64(&out[i + 0], neon_intrinsic(vld1q_u64(&array_1[i + 0]), \ + vld1q_u64(&array_2[i + 0]))); \ + vst1q_u64(&out[i + 2], neon_intrinsic(vld1q_u64(&array_1[i + 2]), \ + vld1q_u64(&array_2[i + 2]))); \ + vst1q_u64(&out[i + 4], neon_intrinsic(vld1q_u64(&array_1[i + 4]), \ + vld1q_u64(&array_2[i + 4]))); \ + vst1q_u64(&out[i + 6], neon_intrinsic(vld1q_u64(&array_1[i + 6]), \ + vld1q_u64(&array_2[i + 6]))); \ + } \ + dst->cardinality = BITSET_UNKNOWN_CARDINALITY; \ + return dst->cardinality; \ +} \ +int bitset_container_##opname##_justcard(const bitset_container_t *src_1, \ + const bitset_container_t *src_2) { \ + const uint64_t * __restrict__ array_1 = src_1->array; \ + const uint64_t * __restrict__ array_2 = src_2->array; \ + uint16x8_t n0 = vdupq_n_u16(0); \ + uint16x8_t n1 = vdupq_n_u16(0); \ + uint16x8_t n2 = vdupq_n_u16(0); \ + uint16x8_t n3 = vdupq_n_u16(0); \ + for (size_t i = 0; i < BITSET_CONTAINER_SIZE_IN_WORDS; i += 8) { \ + uint64x2_t c0 = neon_intrinsic(vld1q_u64(&array_1[i + 0]), \ + vld1q_u64(&array_2[i + 0])); \ + n0 = vaddq_u16(n0, vpaddlq_u8(vcntq_u8(vreinterpretq_u8_u64(c0)))); \ + uint64x2_t c1 = neon_intrinsic(vld1q_u64(&array_1[i + 2]), \ + vld1q_u64(&array_2[i + 2])); \ + n1 = vaddq_u16(n1, vpaddlq_u8(vcntq_u8(vreinterpretq_u8_u64(c1)))); \ + uint64x2_t c2 = neon_intrinsic(vld1q_u64(&array_1[i + 4]), \ + vld1q_u64(&array_2[i + 4])); \ + n2 = vaddq_u16(n2, vpaddlq_u8(vcntq_u8(vreinterpretq_u8_u64(c2)))); \ + uint64x2_t c3 = neon_intrinsic(vld1q_u64(&array_1[i + 6]), \ + vld1q_u64(&array_2[i + 6])); \ + n3 = vaddq_u16(n3, vpaddlq_u8(vcntq_u8(vreinterpretq_u8_u64(c3)))); \ + } \ + uint64x2_t n = vdupq_n_u64(0); \ + n = vaddq_u64(n, vpaddlq_u32(vpaddlq_u16(n0))); \ + n = vaddq_u64(n, vpaddlq_u32(vpaddlq_u16(n1))); \ + n = vaddq_u64(n, vpaddlq_u32(vpaddlq_u16(n2))); \ + n = vaddq_u64(n, vpaddlq_u32(vpaddlq_u16(n3))); \ + return vgetq_lane_u64(n, 0) + vgetq_lane_u64(n, 1); \ +} + +#else /* not USEAVX */ + +#define BITSET_CONTAINER_FN(opname, opsymbol, avx_intrinsic, neon_intrinsic) \ +int bitset_container_##opname(const bitset_container_t *src_1, \ + const bitset_container_t *src_2, \ + bitset_container_t *dst) { \ + const uint64_t * __restrict__ array_1 = src_1->array; \ + const uint64_t * __restrict__ array_2 = src_2->array; \ + uint64_t *out = dst->array; \ + int32_t sum = 0; \ + for (size_t i = 0; i < BITSET_CONTAINER_SIZE_IN_WORDS; i += 2) { \ + const uint64_t word_1 = (array_1[i])opsymbol(array_2[i]), \ + word_2 = (array_1[i + 1])opsymbol(array_2[i + 1]); \ + out[i] = word_1; \ + out[i + 1] = word_2; \ + sum += hamming(word_1); \ + sum += hamming(word_2); \ + } \ + dst->cardinality = sum; \ + return dst->cardinality; \ +} \ +int bitset_container_##opname##_nocard(const bitset_container_t *src_1, \ + const bitset_container_t *src_2, \ + bitset_container_t *dst) { \ + const uint64_t * __restrict__ array_1 = src_1->array; \ + const uint64_t * __restrict__ array_2 = src_2->array; \ + uint64_t *out = dst->array; \ + for (size_t i = 0; i < BITSET_CONTAINER_SIZE_IN_WORDS; i++) { \ + out[i] = (array_1[i])opsymbol(array_2[i]); \ + } \ + dst->cardinality = BITSET_UNKNOWN_CARDINALITY; \ + return dst->cardinality; \ +} \ +int bitset_container_##opname##_justcard(const bitset_container_t *src_1, \ + const bitset_container_t *src_2) { \ + const uint64_t * __restrict__ array_1 = src_1->array; \ + const uint64_t * __restrict__ array_2 = src_2->array; \ + int32_t sum = 0; \ + for (size_t i = 0; i < BITSET_CONTAINER_SIZE_IN_WORDS; i += 2) { \ + const uint64_t word_1 = (array_1[i])opsymbol(array_2[i]), \ + word_2 = (array_1[i + 1])opsymbol(array_2[i + 1]); \ + sum += hamming(word_1); \ + sum += hamming(word_2); \ + } \ + return sum; \ +} + +#endif + +// we duplicate the function because other containers use the "or" term, makes API more consistent +BITSET_CONTAINER_FN(or, |, _mm256_or_si256, vorrq_u64) +BITSET_CONTAINER_FN(union, |, _mm256_or_si256, vorrq_u64) + +// we duplicate the function because other containers use the "intersection" term, makes API more consistent +BITSET_CONTAINER_FN(and, &, _mm256_and_si256, vandq_u64) +BITSET_CONTAINER_FN(intersection, &, _mm256_and_si256, vandq_u64) + +BITSET_CONTAINER_FN(xor, ^, _mm256_xor_si256, veorq_u64) +BITSET_CONTAINER_FN(andnot, &~, _mm256_andnot_si256, vbicq_u64) +// clang-format On + + + +int bitset_container_to_uint32_array( void *vout, const bitset_container_t *cont, uint32_t base) { +#ifdef USEAVX2FORDECODING + if(cont->cardinality >= 8192)// heuristic + return (int) bitset_extract_setbits_avx2(cont->array, BITSET_CONTAINER_SIZE_IN_WORDS, vout,cont->cardinality,base); + else + return (int) bitset_extract_setbits(cont->array, BITSET_CONTAINER_SIZE_IN_WORDS, vout,base); +#else + return (int) bitset_extract_setbits(cont->array, BITSET_CONTAINER_SIZE_IN_WORDS, vout,base); +#endif +} + +/* + * Print this container using printf (useful for debugging). + */ +void bitset_container_printf(const bitset_container_t * v) { + printf("{"); + uint32_t base = 0; + bool iamfirst = true;// TODO: rework so that this is not necessary yet still readable + for (int i = 0; i < BITSET_CONTAINER_SIZE_IN_WORDS; ++i) { + uint64_t w = v->array[i]; + while (w != 0) { + uint64_t t = w & (~w + 1); + int r = __builtin_ctzll(w); + if(iamfirst) {// predicted to be false + printf("%u",base + r); + iamfirst = false; + } else { + printf(",%u",base + r); + } + w ^= t; + } + base += 64; + } + printf("}"); +} + + +/* + * Print this container using printf as a comma-separated list of 32-bit integers starting at base. + */ +void bitset_container_printf_as_uint32_array(const bitset_container_t * v, uint32_t base) { + bool iamfirst = true;// TODO: rework so that this is not necessary yet still readable + for (int i = 0; i < BITSET_CONTAINER_SIZE_IN_WORDS; ++i) { + uint64_t w = v->array[i]; + while (w != 0) { + uint64_t t = w & (~w + 1); + int r = __builtin_ctzll(w); + if(iamfirst) {// predicted to be false + printf("%u", r + base); + iamfirst = false; + } else { + printf(",%u",r + base); + } + w ^= t; + } + base += 64; + } +} + + +// TODO: use the fast lower bound, also +int bitset_container_number_of_runs(bitset_container_t *b) { + int num_runs = 0; + uint64_t next_word = b->array[0]; + + for (int i = 0; i < BITSET_CONTAINER_SIZE_IN_WORDS-1; ++i) { + uint64_t word = next_word; + next_word = b->array[i+1]; + num_runs += hamming((~word) & (word << 1)) + ( (word >> 63) & ~next_word); + } + + uint64_t word = next_word; + num_runs += hamming((~word) & (word << 1)); + if((word & 0x8000000000000000ULL) != 0) + num_runs++; + return num_runs; +} + +int32_t bitset_container_serialize(const bitset_container_t *container, char *buf) { + int32_t l = sizeof(uint64_t) * BITSET_CONTAINER_SIZE_IN_WORDS; + memcpy(buf, container->array, l); + return(l); +} + + + +int32_t bitset_container_write(const bitset_container_t *container, + char *buf) { + memcpy(buf, container->array, BITSET_CONTAINER_SIZE_IN_WORDS * sizeof(uint64_t)); + return bitset_container_size_in_bytes(container); +} + + +int32_t bitset_container_read(int32_t cardinality, bitset_container_t *container, + const char *buf) { + container->cardinality = cardinality; + memcpy(container->array, buf, BITSET_CONTAINER_SIZE_IN_WORDS * sizeof(uint64_t)); + return bitset_container_size_in_bytes(container); +} + +uint32_t bitset_container_serialization_len(void) { + return(sizeof(uint64_t) * BITSET_CONTAINER_SIZE_IN_WORDS); +} + +void* bitset_container_deserialize(const char *buf, size_t buf_len) { + bitset_container_t *ptr; + size_t l = sizeof(uint64_t) * BITSET_CONTAINER_SIZE_IN_WORDS; + + if(l != buf_len) + return(NULL); + + if((ptr = (bitset_container_t *)malloc(sizeof(bitset_container_t))) != NULL) { + memcpy(ptr, buf, sizeof(bitset_container_t)); + // sizeof(__m256i) == 32 + ptr->array = (uint64_t *) roaring_bitmap_aligned_malloc(32, l); + if (! ptr->array) { + free(ptr); + return NULL; + } + memcpy(ptr->array, buf, l); + ptr->cardinality = bitset_container_compute_cardinality(ptr); + } + + return((void*)ptr); +} + +bool bitset_container_iterate(const bitset_container_t *cont, uint32_t base, roaring_iterator iterator, void *ptr) { + for (int32_t i = 0; i < BITSET_CONTAINER_SIZE_IN_WORDS; ++i ) { + uint64_t w = cont->array[i]; + while (w != 0) { + uint64_t t = w & (~w + 1); + int r = __builtin_ctzll(w); + if(!iterator(r + base, ptr)) return false; + w ^= t; + } + base += 64; + } + return true; +} + +bool bitset_container_iterate64(const bitset_container_t *cont, uint32_t base, roaring_iterator64 iterator, uint64_t high_bits, void *ptr) { + for (int32_t i = 0; i < BITSET_CONTAINER_SIZE_IN_WORDS; ++i ) { + uint64_t w = cont->array[i]; + while (w != 0) { + uint64_t t = w & (~w + 1); + int r = __builtin_ctzll(w); + if(!iterator(high_bits | (uint64_t)(r + base), ptr)) return false; + w ^= t; + } + base += 64; + } + return true; +} + + +bool bitset_container_equals(const bitset_container_t *container1, const bitset_container_t *container2) { + if((container1->cardinality != BITSET_UNKNOWN_CARDINALITY) && (container2->cardinality != BITSET_UNKNOWN_CARDINALITY)) { + if(container1->cardinality != container2->cardinality) { + return false; + } + if (container1->cardinality == INT32_C(0x10000)) { + return true; + } + } +#ifdef USEAVX + const __m256i *ptr1 = (const __m256i*)container1->array; + const __m256i *ptr2 = (const __m256i*)container2->array; + for (size_t i = 0; i < BITSET_CONTAINER_SIZE_IN_WORDS*sizeof(uint64_t)/32; i++) { + __m256i r1 = _mm256_load_si256(ptr1+i); + __m256i r2 = _mm256_load_si256(ptr2+i); + int mask = _mm256_movemask_epi8(_mm256_cmpeq_epi8(r1, r2)); + if ((uint32_t)mask != UINT32_MAX) { + return false; + } + } +#else + return memcmp(container1->array, + container2->array, + BITSET_CONTAINER_SIZE_IN_WORDS*sizeof(uint64_t)) == 0; +#endif + return true; +} + +bool bitset_container_is_subset(const bitset_container_t *container1, + const bitset_container_t *container2) { + if((container1->cardinality != BITSET_UNKNOWN_CARDINALITY) && (container2->cardinality != BITSET_UNKNOWN_CARDINALITY)) { + if(container1->cardinality > container2->cardinality) { + return false; + } + } + for(int32_t i = 0; i < BITSET_CONTAINER_SIZE_IN_WORDS; ++i ) { + if((container1->array[i] & container2->array[i]) != container1->array[i]) { + return false; + } + } + return true; +} + +bool bitset_container_select(const bitset_container_t *container, uint32_t *start_rank, uint32_t rank, uint32_t *element) { + int card = bitset_container_cardinality(container); + if(rank >= *start_rank + card) { + *start_rank += card; + return false; + } + const uint64_t *array = container->array; + int32_t size; + for (int i = 0; i < BITSET_CONTAINER_SIZE_IN_WORDS; i += 1) { + size = hamming(array[i]); + if(rank <= *start_rank + size) { + uint64_t w = container->array[i]; + uint16_t base = i*64; + while (w != 0) { + uint64_t t = w & (~w + 1); + int r = __builtin_ctzll(w); + if(*start_rank == rank) { + *element = r+base; + return true; + } + w ^= t; + *start_rank += 1; + } + } + else + *start_rank += size; + } + assert(false); + __builtin_unreachable(); +} + + +/* Returns the smallest value (assumes not empty) */ +uint16_t bitset_container_minimum(const bitset_container_t *container) { + for (int32_t i = 0; i < BITSET_CONTAINER_SIZE_IN_WORDS; ++i ) { + uint64_t w = container->array[i]; + if (w != 0) { + int r = __builtin_ctzll(w); + return r + i * 64; + } + } + return UINT16_MAX; +} + +/* Returns the largest value (assumes not empty) */ +uint16_t bitset_container_maximum(const bitset_container_t *container) { + for (int32_t i = BITSET_CONTAINER_SIZE_IN_WORDS - 1; i > 0; --i ) { + uint64_t w = container->array[i]; + if (w != 0) { + int r = __builtin_clzll(w); + return i * 64 + 63 - r; + } + } + return 0; +} + +/* Returns the number of values equal or smaller than x */ +int bitset_container_rank(const bitset_container_t *container, uint16_t x) { + // credit: aqrit + int sum = 0; + int i = 0; + for (int end = x / 64; i < end; i++){ + sum += hamming(container->array[i]); + } + uint64_t lastword = container->array[i]; + uint64_t lastpos = UINT64_C(1) << (x % 64); + uint64_t mask = lastpos + lastpos - 1; // smear right + sum += hamming(lastword & mask); + return sum; +} + +/* Returns the index of the first value equal or larger than x, or -1 */ +int bitset_container_index_equalorlarger(const bitset_container_t *container, uint16_t x) { + uint32_t x32 = x; + uint32_t k = x32 / 64; + uint64_t word = container->array[k]; + const int diff = x32 - k * 64; // in [0,64) + word = (word >> diff) << diff; // a mask is faster, but we don't care + while(word == 0) { + k++; + if(k == BITSET_CONTAINER_SIZE_IN_WORDS) return -1; + word = container->array[k]; + } + return k * 64 + __builtin_ctzll(word); +} +/* end file src/containers/bitset.c */ +/* begin file src/containers/containers.c */ + + +void container_free(void *container, uint8_t typecode) { + switch (typecode) { + case BITSET_CONTAINER_TYPE_CODE: + bitset_container_free((bitset_container_t *)container); + break; + case ARRAY_CONTAINER_TYPE_CODE: + array_container_free((array_container_t *)container); + break; + case RUN_CONTAINER_TYPE_CODE: + run_container_free((run_container_t *)container); + break; + case SHARED_CONTAINER_TYPE_CODE: + shared_container_free((shared_container_t *)container); + break; + default: + assert(false); + __builtin_unreachable(); + } +} + +void container_printf(const void *container, uint8_t typecode) { + container = container_unwrap_shared(container, &typecode); + switch (typecode) { + case BITSET_CONTAINER_TYPE_CODE: + bitset_container_printf((const bitset_container_t *)container); + return; + case ARRAY_CONTAINER_TYPE_CODE: + array_container_printf((const array_container_t *)container); + return; + case RUN_CONTAINER_TYPE_CODE: + run_container_printf((const run_container_t *)container); + return; + default: + __builtin_unreachable(); + } +} + +void container_printf_as_uint32_array(const void *container, uint8_t typecode, + uint32_t base) { + container = container_unwrap_shared(container, &typecode); + switch (typecode) { + case BITSET_CONTAINER_TYPE_CODE: + bitset_container_printf_as_uint32_array( + (const bitset_container_t *)container, base); + return; + case ARRAY_CONTAINER_TYPE_CODE: + array_container_printf_as_uint32_array( + (const array_container_t *)container, base); + return; + case RUN_CONTAINER_TYPE_CODE: + run_container_printf_as_uint32_array( + (const run_container_t *)container, base); + return; + return; + default: + __builtin_unreachable(); + } +} + +int32_t container_serialize(const void *container, uint8_t typecode, + char *buf) { + container = container_unwrap_shared(container, &typecode); + switch (typecode) { + case BITSET_CONTAINER_TYPE_CODE: + return (bitset_container_serialize((const bitset_container_t *)container, + buf)); + case ARRAY_CONTAINER_TYPE_CODE: + return ( + array_container_serialize((const array_container_t *)container, buf)); + case RUN_CONTAINER_TYPE_CODE: + return (run_container_serialize((const run_container_t *)container, buf)); + default: + assert(0); + __builtin_unreachable(); + return (-1); + } +} + +uint32_t container_serialization_len(const void *container, uint8_t typecode) { + container = container_unwrap_shared(container, &typecode); + switch (typecode) { + case BITSET_CONTAINER_TYPE_CODE: + return bitset_container_serialization_len(); + case ARRAY_CONTAINER_TYPE_CODE: + return array_container_serialization_len( + (const array_container_t *)container); + case RUN_CONTAINER_TYPE_CODE: + return run_container_serialization_len( + (const run_container_t *)container); + default: + assert(0); + __builtin_unreachable(); + return (0); + } +} + +void *container_deserialize(uint8_t typecode, const char *buf, size_t buf_len) { + switch (typecode) { + case BITSET_CONTAINER_TYPE_CODE: + return (bitset_container_deserialize(buf, buf_len)); + case ARRAY_CONTAINER_TYPE_CODE: + return (array_container_deserialize(buf, buf_len)); + case RUN_CONTAINER_TYPE_CODE: + return (run_container_deserialize(buf, buf_len)); + case SHARED_CONTAINER_TYPE_CODE: + printf("this should never happen.\n"); + assert(0); + __builtin_unreachable(); + return (NULL); + default: + assert(0); + __builtin_unreachable(); + return (NULL); + } +} + +void *get_copy_of_container(void *container, uint8_t *typecode, + bool copy_on_write) { + if (copy_on_write) { + shared_container_t *shared_container; + if (*typecode == SHARED_CONTAINER_TYPE_CODE) { + shared_container = (shared_container_t *)container; + shared_container->counter += 1; + return shared_container; + } + assert(*typecode != SHARED_CONTAINER_TYPE_CODE); + + if ((shared_container = (shared_container_t *)malloc( + sizeof(shared_container_t))) == NULL) { + return NULL; + } + + shared_container->container = container; + shared_container->typecode = *typecode; + + shared_container->counter = 2; + *typecode = SHARED_CONTAINER_TYPE_CODE; + + return shared_container; + } // copy_on_write + // otherwise, no copy on write... + const void *actualcontainer = + container_unwrap_shared((const void *)container, typecode); + assert(*typecode != SHARED_CONTAINER_TYPE_CODE); + return container_clone(actualcontainer, *typecode); +} +/** + * Copies a container, requires a typecode. This allocates new memory, caller + * is responsible for deallocation. + */ +void *container_clone(const void *container, uint8_t typecode) { + container = container_unwrap_shared(container, &typecode); + switch (typecode) { + case BITSET_CONTAINER_TYPE_CODE: + return bitset_container_clone((const bitset_container_t *)container); + case ARRAY_CONTAINER_TYPE_CODE: + return array_container_clone((const array_container_t *)container); + case RUN_CONTAINER_TYPE_CODE: + return run_container_clone((const run_container_t *)container); + case SHARED_CONTAINER_TYPE_CODE: + printf("shared containers are not clonable\n"); + assert(false); + return NULL; + default: + assert(false); + __builtin_unreachable(); + return NULL; + } +} + +void *shared_container_extract_copy(shared_container_t *container, + uint8_t *typecode) { + assert(container->counter > 0); + assert(container->typecode != SHARED_CONTAINER_TYPE_CODE); + container->counter--; + *typecode = container->typecode; + void *answer; + if (container->counter == 0) { + answer = container->container; + container->container = NULL; // paranoid + free(container); + } else { + answer = container_clone(container->container, *typecode); + } + assert(*typecode != SHARED_CONTAINER_TYPE_CODE); + return answer; +} + +void shared_container_free(shared_container_t *container) { + assert(container->counter > 0); + container->counter--; + if (container->counter == 0) { + assert(container->typecode != SHARED_CONTAINER_TYPE_CODE); + container_free(container->container, container->typecode); + container->container = NULL; // paranoid + free(container); + } +} + +/* end file src/containers/containers.c */ +/* begin file src/containers/convert.c */ +#include + + +// file contains grubby stuff that must know impl. details of all container +// types. +bitset_container_t *bitset_container_from_array(const array_container_t *a) { + bitset_container_t *ans = bitset_container_create(); + int limit = array_container_cardinality(a); + for (int i = 0; i < limit; ++i) bitset_container_set(ans, a->array[i]); + return ans; +} + +bitset_container_t *bitset_container_from_run(const run_container_t *arr) { + int card = run_container_cardinality(arr); + bitset_container_t *answer = bitset_container_create(); + for (int rlepos = 0; rlepos < arr->n_runs; ++rlepos) { + rle16_t vl = arr->runs[rlepos]; + bitset_set_lenrange(answer->array, vl.value, vl.length); + } + answer->cardinality = card; + return answer; +} + +array_container_t *array_container_from_run(const run_container_t *arr) { + array_container_t *answer = + array_container_create_given_capacity(run_container_cardinality(arr)); + answer->cardinality = 0; + for (int rlepos = 0; rlepos < arr->n_runs; ++rlepos) { + int run_start = arr->runs[rlepos].value; + int run_end = run_start + arr->runs[rlepos].length; + + for (int run_value = run_start; run_value <= run_end; ++run_value) { + answer->array[answer->cardinality++] = (uint16_t)run_value; + } + } + return answer; +} + +array_container_t *array_container_from_bitset(const bitset_container_t *bits) { + array_container_t *result = + array_container_create_given_capacity(bits->cardinality); + result->cardinality = bits->cardinality; + // sse version ends up being slower here + // (bitset_extract_setbits_sse_uint16) + // because of the sparsity of the data + bitset_extract_setbits_uint16(bits->array, BITSET_CONTAINER_SIZE_IN_WORDS, + result->array, 0); + return result; +} + +/* assumes that container has adequate space. Run from [s,e] (inclusive) */ +static void add_run(run_container_t *r, int s, int e) { + r->runs[r->n_runs].value = s; + r->runs[r->n_runs].length = e - s; + r->n_runs++; +} + +run_container_t *run_container_from_array(const array_container_t *c) { + int32_t n_runs = array_container_number_of_runs(c); + run_container_t *answer = run_container_create_given_capacity(n_runs); + int prev = -2; + int run_start = -1; + int32_t card = c->cardinality; + if (card == 0) return answer; + for (int i = 0; i < card; ++i) { + const uint16_t cur_val = c->array[i]; + if (cur_val != prev + 1) { + // new run starts; flush old one, if any + if (run_start != -1) add_run(answer, run_start, prev); + run_start = cur_val; + } + prev = c->array[i]; + } + // now prev is the last seen value + add_run(answer, run_start, prev); + // assert(run_container_cardinality(answer) == c->cardinality); + return answer; +} + +/** + * Convert the runcontainer to either a Bitmap or an Array Container, depending + * on the cardinality. Frees the container. + * Allocates and returns new container, which caller is responsible for freeing. + * It does not free the run container. + */ + +void *convert_to_bitset_or_array_container(run_container_t *r, int32_t card, + uint8_t *resulttype) { + if (card <= DEFAULT_MAX_SIZE) { + array_container_t *answer = array_container_create_given_capacity(card); + answer->cardinality = 0; + for (int rlepos = 0; rlepos < r->n_runs; ++rlepos) { + uint16_t run_start = r->runs[rlepos].value; + uint16_t run_end = run_start + r->runs[rlepos].length; + for (uint16_t run_value = run_start; run_value <= run_end; + ++run_value) { + answer->array[answer->cardinality++] = run_value; + } + } + assert(card == answer->cardinality); + *resulttype = ARRAY_CONTAINER_TYPE_CODE; + //run_container_free(r); + return answer; + } + bitset_container_t *answer = bitset_container_create(); + for (int rlepos = 0; rlepos < r->n_runs; ++rlepos) { + uint16_t run_start = r->runs[rlepos].value; + bitset_set_lenrange(answer->array, run_start, r->runs[rlepos].length); + } + answer->cardinality = card; + *resulttype = BITSET_CONTAINER_TYPE_CODE; + //run_container_free(r); + return answer; +} + +/* Converts a run container to either an array or a bitset, IF it saves space. + */ +/* If a conversion occurs, the caller is responsible to free the original + * container and + * he becomes responsible to free the new one. */ +void *convert_run_to_efficient_container(run_container_t *c, + uint8_t *typecode_after) { + int32_t size_as_run_container = + run_container_serialized_size_in_bytes(c->n_runs); + + int32_t size_as_bitset_container = + bitset_container_serialized_size_in_bytes(); + int32_t card = run_container_cardinality(c); + int32_t size_as_array_container = + array_container_serialized_size_in_bytes(card); + + int32_t min_size_non_run = + size_as_bitset_container < size_as_array_container + ? size_as_bitset_container + : size_as_array_container; + if (size_as_run_container <= min_size_non_run) { // no conversion + *typecode_after = RUN_CONTAINER_TYPE_CODE; + return c; + } + if (card <= DEFAULT_MAX_SIZE) { + // to array + array_container_t *answer = array_container_create_given_capacity(card); + answer->cardinality = 0; + for (int rlepos = 0; rlepos < c->n_runs; ++rlepos) { + int run_start = c->runs[rlepos].value; + int run_end = run_start + c->runs[rlepos].length; + + for (int run_value = run_start; run_value <= run_end; ++run_value) { + answer->array[answer->cardinality++] = (uint16_t)run_value; + } + } + *typecode_after = ARRAY_CONTAINER_TYPE_CODE; + return answer; + } + + // else to bitset + bitset_container_t *answer = bitset_container_create(); + + for (int rlepos = 0; rlepos < c->n_runs; ++rlepos) { + int start = c->runs[rlepos].value; + int end = start + c->runs[rlepos].length; + bitset_set_range(answer->array, start, end + 1); + } + answer->cardinality = card; + *typecode_after = BITSET_CONTAINER_TYPE_CODE; + return answer; +} + +// like convert_run_to_efficient_container but frees the old result if needed +void *convert_run_to_efficient_container_and_free(run_container_t *c, + uint8_t *typecode_after) { + void *answer = convert_run_to_efficient_container(c, typecode_after); + if (answer != c) run_container_free(c); + return answer; +} + +/* once converted, the original container is disposed here, rather than + in roaring_array +*/ + +// TODO: split into run- array- and bitset- subfunctions for sanity; +// a few function calls won't really matter. + +void *convert_run_optimize(void *c, uint8_t typecode_original, + uint8_t *typecode_after) { + if (typecode_original == RUN_CONTAINER_TYPE_CODE) { + void *newc = convert_run_to_efficient_container((run_container_t *)c, + typecode_after); + if (newc != c) { + container_free(c, typecode_original); + } + return newc; + } else if (typecode_original == ARRAY_CONTAINER_TYPE_CODE) { + // it might need to be converted to a run container. + array_container_t *c_qua_array = (array_container_t *)c; + int32_t n_runs = array_container_number_of_runs(c_qua_array); + int32_t size_as_run_container = + run_container_serialized_size_in_bytes(n_runs); + int32_t card = array_container_cardinality(c_qua_array); + int32_t size_as_array_container = + array_container_serialized_size_in_bytes(card); + + if (size_as_run_container >= size_as_array_container) { + *typecode_after = ARRAY_CONTAINER_TYPE_CODE; + return c; + } + // else convert array to run container + run_container_t *answer = run_container_create_given_capacity(n_runs); + int prev = -2; + int run_start = -1; + + assert(card > 0); + for (int i = 0; i < card; ++i) { + uint16_t cur_val = c_qua_array->array[i]; + if (cur_val != prev + 1) { + // new run starts; flush old one, if any + if (run_start != -1) add_run(answer, run_start, prev); + run_start = cur_val; + } + prev = c_qua_array->array[i]; + } + assert(run_start >= 0); + // now prev is the last seen value + add_run(answer, run_start, prev); + *typecode_after = RUN_CONTAINER_TYPE_CODE; + array_container_free(c_qua_array); + return answer; + } else if (typecode_original == + BITSET_CONTAINER_TYPE_CODE) { // run conversions on bitset + // does bitset need conversion to run? + bitset_container_t *c_qua_bitset = (bitset_container_t *)c; + int32_t n_runs = bitset_container_number_of_runs(c_qua_bitset); + int32_t size_as_run_container = + run_container_serialized_size_in_bytes(n_runs); + int32_t size_as_bitset_container = + bitset_container_serialized_size_in_bytes(); + + if (size_as_bitset_container <= size_as_run_container) { + // no conversion needed. + *typecode_after = BITSET_CONTAINER_TYPE_CODE; + return c; + } + // bitset to runcontainer (ported from Java RunContainer( + // BitmapContainer bc, int nbrRuns)) + assert(n_runs > 0); // no empty bitmaps + run_container_t *answer = run_container_create_given_capacity(n_runs); + + int long_ctr = 0; + uint64_t cur_word = c_qua_bitset->array[0]; + int run_count = 0; + while (true) { + while (cur_word == UINT64_C(0) && + long_ctr < BITSET_CONTAINER_SIZE_IN_WORDS - 1) + cur_word = c_qua_bitset->array[++long_ctr]; + + if (cur_word == UINT64_C(0)) { + bitset_container_free(c_qua_bitset); + *typecode_after = RUN_CONTAINER_TYPE_CODE; + return answer; + } + + int local_run_start = __builtin_ctzll(cur_word); + int run_start = local_run_start + 64 * long_ctr; + uint64_t cur_word_with_1s = cur_word | (cur_word - 1); + + int run_end = 0; + while (cur_word_with_1s == UINT64_C(0xFFFFFFFFFFFFFFFF) && + long_ctr < BITSET_CONTAINER_SIZE_IN_WORDS - 1) + cur_word_with_1s = c_qua_bitset->array[++long_ctr]; + + if (cur_word_with_1s == UINT64_C(0xFFFFFFFFFFFFFFFF)) { + run_end = 64 + long_ctr * 64; // exclusive, I guess + add_run(answer, run_start, run_end - 1); + bitset_container_free(c_qua_bitset); + *typecode_after = RUN_CONTAINER_TYPE_CODE; + return answer; + } + int local_run_end = __builtin_ctzll(~cur_word_with_1s); + run_end = local_run_end + long_ctr * 64; + add_run(answer, run_start, run_end - 1); + run_count++; + cur_word = cur_word_with_1s & (cur_word_with_1s + 1); + } + return answer; + } else { + assert(false); + __builtin_unreachable(); + return NULL; + } +} + +bitset_container_t *bitset_container_from_run_range(const run_container_t *run, + uint32_t min, uint32_t max) { + bitset_container_t *bitset = bitset_container_create(); + int32_t union_cardinality = 0; + for (int32_t i = 0; i < run->n_runs; ++i) { + uint32_t rle_min = run->runs[i].value; + uint32_t rle_max = rle_min + run->runs[i].length; + bitset_set_lenrange(bitset->array, rle_min, rle_max - rle_min); + union_cardinality += run->runs[i].length + 1; + } + union_cardinality += max - min + 1; + union_cardinality -= bitset_lenrange_cardinality(bitset->array, min, max-min); + bitset_set_lenrange(bitset->array, min, max - min); + bitset->cardinality = union_cardinality; + return bitset; +} +/* end file src/containers/convert.c */ +/* begin file src/containers/mixed_andnot.c */ +/* + * mixed_andnot.c. More methods since operation is not symmetric, + * except no "wide" andnot , so no lazy options motivated. + */ + +#include +#include + + +/* Compute the andnot of src_1 and src_2 and write the result to + * dst, a valid array container that could be the same as dst.*/ +void array_bitset_container_andnot(const array_container_t *src_1, + const bitset_container_t *src_2, + array_container_t *dst) { + // follows Java implementation as of June 2016 + if (dst->capacity < src_1->cardinality) { + array_container_grow(dst, src_1->cardinality, false); + } + int32_t newcard = 0; + const int32_t origcard = src_1->cardinality; + for (int i = 0; i < origcard; ++i) { + uint16_t key = src_1->array[i]; + dst->array[newcard] = key; + newcard += 1 - bitset_container_contains(src_2, key); + } + dst->cardinality = newcard; +} + +/* Compute the andnot of src_1 and src_2 and write the result to + * src_1 */ + +void array_bitset_container_iandnot(array_container_t *src_1, + const bitset_container_t *src_2) { + array_bitset_container_andnot(src_1, src_2, src_1); +} + +/* Compute the andnot of src_1 and src_2 and write the result to + * dst, which does not initially have a valid container. + * Return true for a bitset result; false for array + */ + +bool bitset_array_container_andnot(const bitset_container_t *src_1, + const array_container_t *src_2, void **dst) { + // Java did this directly, but we have option of asm or avx + bitset_container_t *result = bitset_container_create(); + bitset_container_copy(src_1, result); + result->cardinality = + (int32_t)bitset_clear_list(result->array, (uint64_t)result->cardinality, + src_2->array, (uint64_t)src_2->cardinality); + + // do required type conversions. + if (result->cardinality <= DEFAULT_MAX_SIZE) { + *dst = array_container_from_bitset(result); + bitset_container_free(result); + return false; + } + *dst = result; + return true; +} + +/* Compute the andnot of src_1 and src_2 and write the result to + * dst (which has no container initially). It will modify src_1 + * to be dst if the result is a bitset. Otherwise, it will + * free src_1 and dst will be a new array container. In both + * cases, the caller is responsible for deallocating dst. + * Returns true iff dst is a bitset */ + +bool bitset_array_container_iandnot(bitset_container_t *src_1, + const array_container_t *src_2, + void **dst) { + *dst = src_1; + src_1->cardinality = + (int32_t)bitset_clear_list(src_1->array, (uint64_t)src_1->cardinality, + src_2->array, (uint64_t)src_2->cardinality); + + if (src_1->cardinality <= DEFAULT_MAX_SIZE) { + *dst = array_container_from_bitset(src_1); + bitset_container_free(src_1); + return false; // not bitset + } else + return true; +} + +/* Compute the andnot of src_1 and src_2 and write the result to + * dst. Result may be either a bitset or an array container + * (returns "result is bitset"). dst does not initially have + * any container, but becomes either a bitset container (return + * result true) or an array container. + */ + +bool run_bitset_container_andnot(const run_container_t *src_1, + const bitset_container_t *src_2, void **dst) { + // follows the Java implementation as of June 2016 + int card = run_container_cardinality(src_1); + if (card <= DEFAULT_MAX_SIZE) { + // must be an array + array_container_t *answer = array_container_create_given_capacity(card); + answer->cardinality = 0; + for (int32_t rlepos = 0; rlepos < src_1->n_runs; ++rlepos) { + rle16_t rle = src_1->runs[rlepos]; + for (int run_value = rle.value; run_value <= rle.value + rle.length; + ++run_value) { + if (!bitset_container_get(src_2, (uint16_t)run_value)) { + answer->array[answer->cardinality++] = (uint16_t)run_value; + } + } + } + *dst = answer; + return false; + } else { // we guess it will be a bitset, though have to check guess when + // done + bitset_container_t *answer = bitset_container_clone(src_2); + + uint32_t last_pos = 0; + for (int32_t rlepos = 0; rlepos < src_1->n_runs; ++rlepos) { + rle16_t rle = src_1->runs[rlepos]; + + uint32_t start = rle.value; + uint32_t end = start + rle.length + 1; + bitset_reset_range(answer->array, last_pos, start); + bitset_flip_range(answer->array, start, end); + last_pos = end; + } + bitset_reset_range(answer->array, last_pos, (uint32_t)(1 << 16)); + + answer->cardinality = bitset_container_compute_cardinality(answer); + + if (answer->cardinality <= DEFAULT_MAX_SIZE) { + *dst = array_container_from_bitset(answer); + bitset_container_free(answer); + return false; // not bitset + } + *dst = answer; + return true; // bitset + } +} + +/* Compute the andnot of src_1 and src_2 and write the result to + * dst. Result may be either a bitset or an array container + * (returns "result is bitset"). dst does not initially have + * any container, but becomes either a bitset container (return + * result true) or an array container. + */ + +bool run_bitset_container_iandnot(run_container_t *src_1, + const bitset_container_t *src_2, void **dst) { + // dummy implementation + bool ans = run_bitset_container_andnot(src_1, src_2, dst); + run_container_free(src_1); + return ans; +} + +/* Compute the andnot of src_1 and src_2 and write the result to + * dst. Result may be either a bitset or an array container + * (returns "result is bitset"). dst does not initially have + * any container, but becomes either a bitset container (return + * result true) or an array container. + */ + +bool bitset_run_container_andnot(const bitset_container_t *src_1, + const run_container_t *src_2, void **dst) { + // follows Java implementation + bitset_container_t *result = bitset_container_create(); + + bitset_container_copy(src_1, result); + for (int32_t rlepos = 0; rlepos < src_2->n_runs; ++rlepos) { + rle16_t rle = src_2->runs[rlepos]; + bitset_reset_range(result->array, rle.value, + rle.value + rle.length + UINT32_C(1)); + } + result->cardinality = bitset_container_compute_cardinality(result); + + if (result->cardinality <= DEFAULT_MAX_SIZE) { + *dst = array_container_from_bitset(result); + bitset_container_free(result); + return false; // not bitset + } + *dst = result; + return true; // bitset +} + +/* Compute the andnot of src_1 and src_2 and write the result to + * dst (which has no container initially). It will modify src_1 + * to be dst if the result is a bitset. Otherwise, it will + * free src_1 and dst will be a new array container. In both + * cases, the caller is responsible for deallocating dst. + * Returns true iff dst is a bitset */ + +bool bitset_run_container_iandnot(bitset_container_t *src_1, + const run_container_t *src_2, void **dst) { + *dst = src_1; + + for (int32_t rlepos = 0; rlepos < src_2->n_runs; ++rlepos) { + rle16_t rle = src_2->runs[rlepos]; + bitset_reset_range(src_1->array, rle.value, + rle.value + rle.length + UINT32_C(1)); + } + src_1->cardinality = bitset_container_compute_cardinality(src_1); + + if (src_1->cardinality <= DEFAULT_MAX_SIZE) { + *dst = array_container_from_bitset(src_1); + bitset_container_free(src_1); + return false; // not bitset + } else + return true; +} + +/* helper. a_out must be a valid array container with adequate capacity. + * Returns the cardinality of the output container. Partly Based on Java + * implementation Util.unsignedDifference. + * + * TODO: Util.unsignedDifference does not use advanceUntil. Is it cheaper + * to avoid advanceUntil? + */ + +static int run_array_array_subtract(const run_container_t *r, + const array_container_t *a_in, + array_container_t *a_out) { + int out_card = 0; + int32_t in_array_pos = + -1; // since advanceUntil always assumes we start the search AFTER this + + for (int rlepos = 0; rlepos < r->n_runs; rlepos++) { + int32_t start = r->runs[rlepos].value; + int32_t end = start + r->runs[rlepos].length + 1; + + in_array_pos = advanceUntil(a_in->array, in_array_pos, + a_in->cardinality, (uint16_t)start); + + if (in_array_pos >= a_in->cardinality) { // run has no items subtracted + for (int32_t i = start; i < end; ++i) + a_out->array[out_card++] = (uint16_t)i; + } else { + uint16_t next_nonincluded = a_in->array[in_array_pos]; + if (next_nonincluded >= end) { + // another case when run goes unaltered + for (int32_t i = start; i < end; ++i) + a_out->array[out_card++] = (uint16_t)i; + in_array_pos--; // ensure we see this item again if necessary + } else { + for (int32_t i = start; i < end; ++i) + if (i != next_nonincluded) + a_out->array[out_card++] = (uint16_t)i; + else // 0 should ensure we don't match + next_nonincluded = + (in_array_pos + 1 >= a_in->cardinality) + ? 0 + : a_in->array[++in_array_pos]; + in_array_pos--; // see again + } + } + } + return out_card; +} + +/* dst does not indicate a valid container initially. Eventually it + * can become any type of container. + */ + +int run_array_container_andnot(const run_container_t *src_1, + const array_container_t *src_2, void **dst) { + // follows the Java impl as of June 2016 + + int card = run_container_cardinality(src_1); + const int arbitrary_threshold = 32; + + if (card <= arbitrary_threshold) { + if (src_2->cardinality == 0) { + *dst = run_container_clone(src_1); + return RUN_CONTAINER_TYPE_CODE; + } + // Java's "lazyandNot.toEfficientContainer" thing + run_container_t *answer = run_container_create_given_capacity( + card + array_container_cardinality(src_2)); + + int rlepos = 0; + int xrlepos = 0; // "x" is src_2 + rle16_t rle = src_1->runs[rlepos]; + int32_t start = rle.value; + int32_t end = start + rle.length + 1; + int32_t xstart = src_2->array[xrlepos]; + + while ((rlepos < src_1->n_runs) && (xrlepos < src_2->cardinality)) { + if (end <= xstart) { + // output the first run + answer->runs[answer->n_runs++] = + (rle16_t){.value = (uint16_t)start, + .length = (uint16_t)(end - start - 1)}; + rlepos++; + if (rlepos < src_1->n_runs) { + start = src_1->runs[rlepos].value; + end = start + src_1->runs[rlepos].length + 1; + } + } else if (xstart + 1 <= start) { + // exit the second run + xrlepos++; + if (xrlepos < src_2->cardinality) { + xstart = src_2->array[xrlepos]; + } + } else { + if (start < xstart) { + answer->runs[answer->n_runs++] = + (rle16_t){.value = (uint16_t)start, + .length = (uint16_t)(xstart - start - 1)}; + } + if (xstart + 1 < end) { + start = xstart + 1; + } else { + rlepos++; + if (rlepos < src_1->n_runs) { + start = src_1->runs[rlepos].value; + end = start + src_1->runs[rlepos].length + 1; + } + } + } + } + if (rlepos < src_1->n_runs) { + answer->runs[answer->n_runs++] = + (rle16_t){.value = (uint16_t)start, + .length = (uint16_t)(end - start - 1)}; + rlepos++; + if (rlepos < src_1->n_runs) { + memcpy(answer->runs + answer->n_runs, src_1->runs + rlepos, + (src_1->n_runs - rlepos) * sizeof(rle16_t)); + answer->n_runs += (src_1->n_runs - rlepos); + } + } + uint8_t return_type; + *dst = convert_run_to_efficient_container(answer, &return_type); + if (answer != *dst) run_container_free(answer); + return return_type; + } + // else it's a bitmap or array + + if (card <= DEFAULT_MAX_SIZE) { + array_container_t *ac = array_container_create_given_capacity(card); + // nb Java code used a generic iterator-based merge to compute + // difference + ac->cardinality = run_array_array_subtract(src_1, src_2, ac); + *dst = ac; + return ARRAY_CONTAINER_TYPE_CODE; + } + bitset_container_t *ans = bitset_container_from_run(src_1); + bool result_is_bitset = bitset_array_container_iandnot(ans, src_2, dst); + return (result_is_bitset ? BITSET_CONTAINER_TYPE_CODE + : ARRAY_CONTAINER_TYPE_CODE); +} + +/* Compute the andnot of src_1 and src_2 and write the result to + * dst (which has no container initially). It will modify src_1 + * to be dst if the result is a bitset. Otherwise, it will + * free src_1 and dst will be a new array container. In both + * cases, the caller is responsible for deallocating dst. + * Returns true iff dst is a bitset */ + +int run_array_container_iandnot(run_container_t *src_1, + const array_container_t *src_2, void **dst) { + // dummy implementation same as June 2016 Java + int ans = run_array_container_andnot(src_1, src_2, dst); + run_container_free(src_1); + return ans; +} + +/* dst must be a valid array container, allowed to be src_1 */ + +void array_run_container_andnot(const array_container_t *src_1, + const run_container_t *src_2, + array_container_t *dst) { + // basically following Java impl as of June 2016 + if (src_1->cardinality > dst->capacity) { + array_container_grow(dst, src_1->cardinality, false); + } + + if (src_2->n_runs == 0) { + memmove(dst->array, src_1->array, + sizeof(uint16_t) * src_1->cardinality); + dst->cardinality = src_1->cardinality; + return; + } + int32_t run_start = src_2->runs[0].value; + int32_t run_end = run_start + src_2->runs[0].length; + int which_run = 0; + + uint16_t val = 0; + int dest_card = 0; + for (int i = 0; i < src_1->cardinality; ++i) { + val = src_1->array[i]; + if (val < run_start) + dst->array[dest_card++] = val; + else if (val <= run_end) { + ; // omitted item + } else { + do { + if (which_run + 1 < src_2->n_runs) { + ++which_run; + run_start = src_2->runs[which_run].value; + run_end = run_start + src_2->runs[which_run].length; + + } else + run_start = run_end = (1 << 16) + 1; + } while (val > run_end); + --i; + } + } + dst->cardinality = dest_card; +} + +/* dst does not indicate a valid container initially. Eventually it + * can become any kind of container. + */ + +void array_run_container_iandnot(array_container_t *src_1, + const run_container_t *src_2) { + array_run_container_andnot(src_1, src_2, src_1); +} + +/* dst does not indicate a valid container initially. Eventually it + * can become any kind of container. + */ + +int run_run_container_andnot(const run_container_t *src_1, + const run_container_t *src_2, void **dst) { + run_container_t *ans = run_container_create(); + run_container_andnot(src_1, src_2, ans); + uint8_t typecode_after; + *dst = convert_run_to_efficient_container_and_free(ans, &typecode_after); + return typecode_after; +} + +/* Compute the andnot of src_1 and src_2 and write the result to + * dst (which has no container initially). It will modify src_1 + * to be dst if the result is a bitset. Otherwise, it will + * free src_1 and dst will be a new array container. In both + * cases, the caller is responsible for deallocating dst. + * Returns true iff dst is a bitset */ + +int run_run_container_iandnot(run_container_t *src_1, + const run_container_t *src_2, void **dst) { + // following Java impl as of June 2016 (dummy) + int ans = run_run_container_andnot(src_1, src_2, dst); + run_container_free(src_1); + return ans; +} + +/* + * dst is a valid array container and may be the same as src_1 + */ + +void array_array_container_andnot(const array_container_t *src_1, + const array_container_t *src_2, + array_container_t *dst) { + array_container_andnot(src_1, src_2, dst); +} + +/* inplace array-array andnot will always be able to reuse the space of + * src_1 */ +void array_array_container_iandnot(array_container_t *src_1, + const array_container_t *src_2) { + array_container_andnot(src_1, src_2, src_1); +} + +/* Compute the andnot of src_1 and src_2 and write the result to + * dst (which has no container initially). Return value is + * "dst is a bitset" + */ + +bool bitset_bitset_container_andnot(const bitset_container_t *src_1, + const bitset_container_t *src_2, + void **dst) { + bitset_container_t *ans = bitset_container_create(); + int card = bitset_container_andnot(src_1, src_2, ans); + if (card <= DEFAULT_MAX_SIZE) { + *dst = array_container_from_bitset(ans); + bitset_container_free(ans); + return false; // not bitset + } else { + *dst = ans; + return true; + } +} + +/* Compute the andnot of src_1 and src_2 and write the result to + * dst (which has no container initially). It will modify src_1 + * to be dst if the result is a bitset. Otherwise, it will + * free src_1 and dst will be a new array container. In both + * cases, the caller is responsible for deallocating dst. + * Returns true iff dst is a bitset */ + +bool bitset_bitset_container_iandnot(bitset_container_t *src_1, + const bitset_container_t *src_2, + void **dst) { + int card = bitset_container_andnot(src_1, src_2, src_1); + if (card <= DEFAULT_MAX_SIZE) { + *dst = array_container_from_bitset(src_1); + bitset_container_free(src_1); + return false; // not bitset + } else { + *dst = src_1; + return true; + } +} +/* end file src/containers/mixed_andnot.c */ +/* begin file src/containers/mixed_equal.c */ + +bool array_container_equal_bitset(const array_container_t* container1, + const bitset_container_t* container2) { + if (container2->cardinality != BITSET_UNKNOWN_CARDINALITY) { + if (container2->cardinality != container1->cardinality) { + return false; + } + } + int32_t pos = 0; + for (int32_t i = 0; i < BITSET_CONTAINER_SIZE_IN_WORDS; ++i) { + uint64_t w = container2->array[i]; + while (w != 0) { + uint64_t t = w & (~w + 1); + uint16_t r = i * 64 + __builtin_ctzll(w); + if (pos >= container1->cardinality) { + return false; + } + if (container1->array[pos] != r) { + return false; + } + ++pos; + w ^= t; + } + } + return (pos == container1->cardinality); +} + +bool run_container_equals_array(const run_container_t* container1, + const array_container_t* container2) { + if (run_container_cardinality(container1) != container2->cardinality) + return false; + int32_t pos = 0; + for (int i = 0; i < container1->n_runs; ++i) { + const uint32_t run_start = container1->runs[i].value; + const uint32_t le = container1->runs[i].length; + + if (container2->array[pos] != run_start) { + return false; + } + + if (container2->array[pos + le] != run_start + le) { + return false; + } + + pos += le + 1; + } + return true; +} + +bool run_container_equals_bitset(const run_container_t* container1, + const bitset_container_t* container2) { + + int run_card = run_container_cardinality(container1); + int bitset_card = (container2->cardinality != BITSET_UNKNOWN_CARDINALITY) ? + container2->cardinality : + bitset_container_compute_cardinality(container2); + if (bitset_card != run_card) { + return false; + } + + for (int32_t i = 0; i < container1->n_runs; i++) { + uint32_t begin = container1->runs[i].value; + if (container1->runs[i].length) { + uint32_t end = begin + container1->runs[i].length + 1; + if (!bitset_container_contains_range(container2, begin, end)) { + return false; + } + } else { + if (!bitset_container_contains(container2, begin)) { + return false; + } + } + } + + return true; +} +/* end file src/containers/mixed_equal.c */ +/* begin file src/containers/mixed_intersection.c */ +/* + * mixed_intersection.c + * + */ + + +/* Compute the intersection of src_1 and src_2 and write the result to + * dst. */ +void array_bitset_container_intersection(const array_container_t *src_1, + const bitset_container_t *src_2, + array_container_t *dst) { + if (dst->capacity < src_1->cardinality) { + array_container_grow(dst, src_1->cardinality, false); + } + int32_t newcard = 0; // dst could be src_1 + const int32_t origcard = src_1->cardinality; + for (int i = 0; i < origcard; ++i) { + uint16_t key = src_1->array[i]; + // this branchless approach is much faster... + dst->array[newcard] = key; + newcard += bitset_container_contains(src_2, key); + /** + * we could do it this way instead... + * if (bitset_container_contains(src_2, key)) { + * dst->array[newcard++] = key; + * } + * but if the result is unpredictable, the processor generates + * many mispredicted branches. + * Difference can be huge (from 3 cycles when predictable all the way + * to 16 cycles when unpredictable. + * See + * https://github.com/lemire/Code-used-on-Daniel-Lemire-s-blog/blob/master/extra/bitset/c/arraybitsetintersection.c + */ + } + dst->cardinality = newcard; +} + +/* Compute the size of the intersection of src_1 and src_2. */ +int array_bitset_container_intersection_cardinality( + const array_container_t *src_1, const bitset_container_t *src_2) { + int32_t newcard = 0; + const int32_t origcard = src_1->cardinality; + for (int i = 0; i < origcard; ++i) { + uint16_t key = src_1->array[i]; + newcard += bitset_container_contains(src_2, key); + } + return newcard; +} + + +bool array_bitset_container_intersect(const array_container_t *src_1, + const bitset_container_t *src_2) { + const int32_t origcard = src_1->cardinality; + for (int i = 0; i < origcard; ++i) { + uint16_t key = src_1->array[i]; + if(bitset_container_contains(src_2, key)) return true; + } + return false; +} + +/* Compute the intersection of src_1 and src_2 and write the result to + * dst. It is allowed for dst to be equal to src_1. We assume that dst is a + * valid container. */ +void array_run_container_intersection(const array_container_t *src_1, + const run_container_t *src_2, + array_container_t *dst) { + if (run_container_is_full(src_2)) { + if (dst != src_1) array_container_copy(src_1, dst); + return; + } + if (dst->capacity < src_1->cardinality) { + array_container_grow(dst, src_1->cardinality, false); + } + if (src_2->n_runs == 0) { + return; + } + int32_t rlepos = 0; + int32_t arraypos = 0; + rle16_t rle = src_2->runs[rlepos]; + int32_t newcard = 0; + while (arraypos < src_1->cardinality) { + const uint16_t arrayval = src_1->array[arraypos]; + while (rle.value + rle.length < + arrayval) { // this will frequently be false + ++rlepos; + if (rlepos == src_2->n_runs) { + dst->cardinality = newcard; + return; // we are done + } + rle = src_2->runs[rlepos]; + } + if (rle.value > arrayval) { + arraypos = advanceUntil(src_1->array, arraypos, src_1->cardinality, + rle.value); + } else { + dst->array[newcard] = arrayval; + newcard++; + arraypos++; + } + } + dst->cardinality = newcard; +} + +/* Compute the intersection of src_1 and src_2 and write the result to + * *dst. If the result is true then the result is a bitset_container_t + * otherwise is a array_container_t. If *dst == src_2, an in-place processing + * is attempted.*/ +bool run_bitset_container_intersection(const run_container_t *src_1, + const bitset_container_t *src_2, + void **dst) { + if (run_container_is_full(src_1)) { + if (*dst != src_2) *dst = bitset_container_clone(src_2); + return true; + } + int32_t card = run_container_cardinality(src_1); + if (card <= DEFAULT_MAX_SIZE) { + // result can only be an array (assuming that we never make a + // RunContainer) + if (card > src_2->cardinality) { + card = src_2->cardinality; + } + array_container_t *answer = array_container_create_given_capacity(card); + *dst = answer; + if (*dst == NULL) { + return false; + } + for (int32_t rlepos = 0; rlepos < src_1->n_runs; ++rlepos) { + rle16_t rle = src_1->runs[rlepos]; + uint32_t endofrun = (uint32_t)rle.value + rle.length; + for (uint32_t runValue = rle.value; runValue <= endofrun; + ++runValue) { + answer->array[answer->cardinality] = (uint16_t)runValue; + answer->cardinality += + bitset_container_contains(src_2, runValue); + } + } + return false; + } + if (*dst == src_2) { // we attempt in-place + bitset_container_t *answer = (bitset_container_t *)*dst; + uint32_t start = 0; + for (int32_t rlepos = 0; rlepos < src_1->n_runs; ++rlepos) { + const rle16_t rle = src_1->runs[rlepos]; + uint32_t end = rle.value; + bitset_reset_range(src_2->array, start, end); + + start = end + rle.length + 1; + } + bitset_reset_range(src_2->array, start, UINT32_C(1) << 16); + answer->cardinality = bitset_container_compute_cardinality(answer); + if (src_2->cardinality > DEFAULT_MAX_SIZE) { + return true; + } else { + array_container_t *newanswer = array_container_from_bitset(src_2); + if (newanswer == NULL) { + *dst = NULL; + return false; + } + *dst = newanswer; + return false; + } + } else { // no inplace + // we expect the answer to be a bitmap (if we are lucky) + bitset_container_t *answer = bitset_container_clone(src_2); + + *dst = answer; + if (answer == NULL) { + return true; + } + uint32_t start = 0; + for (int32_t rlepos = 0; rlepos < src_1->n_runs; ++rlepos) { + const rle16_t rle = src_1->runs[rlepos]; + uint32_t end = rle.value; + bitset_reset_range(answer->array, start, end); + start = end + rle.length + 1; + } + bitset_reset_range(answer->array, start, UINT32_C(1) << 16); + answer->cardinality = bitset_container_compute_cardinality(answer); + + if (answer->cardinality > DEFAULT_MAX_SIZE) { + return true; + } else { + array_container_t *newanswer = array_container_from_bitset(answer); + bitset_container_free((bitset_container_t *)*dst); + if (newanswer == NULL) { + *dst = NULL; + return false; + } + *dst = newanswer; + return false; + } + } +} + +/* Compute the size of the intersection between src_1 and src_2 . */ +int array_run_container_intersection_cardinality(const array_container_t *src_1, + const run_container_t *src_2) { + if (run_container_is_full(src_2)) { + return src_1->cardinality; + } + if (src_2->n_runs == 0) { + return 0; + } + int32_t rlepos = 0; + int32_t arraypos = 0; + rle16_t rle = src_2->runs[rlepos]; + int32_t newcard = 0; + while (arraypos < src_1->cardinality) { + const uint16_t arrayval = src_1->array[arraypos]; + while (rle.value + rle.length < + arrayval) { // this will frequently be false + ++rlepos; + if (rlepos == src_2->n_runs) { + return newcard; // we are done + } + rle = src_2->runs[rlepos]; + } + if (rle.value > arrayval) { + arraypos = advanceUntil(src_1->array, arraypos, src_1->cardinality, + rle.value); + } else { + newcard++; + arraypos++; + } + } + return newcard; +} + +/* Compute the intersection between src_1 and src_2 + **/ +int run_bitset_container_intersection_cardinality( + const run_container_t *src_1, const bitset_container_t *src_2) { + if (run_container_is_full(src_1)) { + return bitset_container_cardinality(src_2); + } + int answer = 0; + for (int32_t rlepos = 0; rlepos < src_1->n_runs; ++rlepos) { + rle16_t rle = src_1->runs[rlepos]; + answer += + bitset_lenrange_cardinality(src_2->array, rle.value, rle.length); + } + return answer; +} + + +bool array_run_container_intersect(const array_container_t *src_1, + const run_container_t *src_2) { + if( run_container_is_full(src_2) ) { + return !array_container_empty(src_1); + } + if (src_2->n_runs == 0) { + return false; + } + int32_t rlepos = 0; + int32_t arraypos = 0; + rle16_t rle = src_2->runs[rlepos]; + while (arraypos < src_1->cardinality) { + const uint16_t arrayval = src_1->array[arraypos]; + while (rle.value + rle.length < + arrayval) { // this will frequently be false + ++rlepos; + if (rlepos == src_2->n_runs) { + return false; // we are done + } + rle = src_2->runs[rlepos]; + } + if (rle.value > arrayval) { + arraypos = advanceUntil(src_1->array, arraypos, src_1->cardinality, + rle.value); + } else { + return true; + } + } + return false; +} + +/* Compute the intersection between src_1 and src_2 + **/ +bool run_bitset_container_intersect(const run_container_t *src_1, + const bitset_container_t *src_2) { + if( run_container_is_full(src_1) ) { + return !bitset_container_empty(src_2); + } + for (int32_t rlepos = 0; rlepos < src_1->n_runs; ++rlepos) { + rle16_t rle = src_1->runs[rlepos]; + if(!bitset_lenrange_empty(src_2->array, rle.value,rle.length)) return true; + } + return false; +} + +/* + * Compute the intersection between src_1 and src_2 and write the result + * to *dst. If the return function is true, the result is a bitset_container_t + * otherwise is a array_container_t. + */ +bool bitset_bitset_container_intersection(const bitset_container_t *src_1, + const bitset_container_t *src_2, + void **dst) { + const int newCardinality = bitset_container_and_justcard(src_1, src_2); + if (newCardinality > DEFAULT_MAX_SIZE) { + *dst = bitset_container_create(); + if (*dst != NULL) { + bitset_container_and_nocard(src_1, src_2, + (bitset_container_t *)*dst); + ((bitset_container_t *)*dst)->cardinality = newCardinality; + } + return true; // it is a bitset + } + *dst = array_container_create_given_capacity(newCardinality); + if (*dst != NULL) { + ((array_container_t *)*dst)->cardinality = newCardinality; + bitset_extract_intersection_setbits_uint16( + ((const bitset_container_t *)src_1)->array, + ((const bitset_container_t *)src_2)->array, + BITSET_CONTAINER_SIZE_IN_WORDS, ((array_container_t *)*dst)->array, + 0); + } + return false; // not a bitset +} + +bool bitset_bitset_container_intersection_inplace( + bitset_container_t *src_1, const bitset_container_t *src_2, void **dst) { + const int newCardinality = bitset_container_and_justcard(src_1, src_2); + if (newCardinality > DEFAULT_MAX_SIZE) { + *dst = src_1; + bitset_container_and_nocard(src_1, src_2, src_1); + ((bitset_container_t *)*dst)->cardinality = newCardinality; + return true; // it is a bitset + } + *dst = array_container_create_given_capacity(newCardinality); + if (*dst != NULL) { + ((array_container_t *)*dst)->cardinality = newCardinality; + bitset_extract_intersection_setbits_uint16( + ((const bitset_container_t *)src_1)->array, + ((const bitset_container_t *)src_2)->array, + BITSET_CONTAINER_SIZE_IN_WORDS, ((array_container_t *)*dst)->array, + 0); + } + return false; // not a bitset +} +/* end file src/containers/mixed_intersection.c */ +/* begin file src/containers/mixed_negation.c */ +/* + * mixed_negation.c + * + */ + +#include +#include + + +// TODO: make simplified and optimized negation code across +// the full range. + +/* Negation across the entire range of the container. + * Compute the negation of src and write the result + * to *dst. The complement of a + * sufficiently sparse set will always be dense and a hence a bitmap +' * We assume that dst is pre-allocated and a valid bitset container + * There can be no in-place version. + */ +void array_container_negation(const array_container_t *src, + bitset_container_t *dst) { + uint64_t card = UINT64_C(1 << 16); + bitset_container_set_all(dst); + + dst->cardinality = (int32_t)bitset_clear_list(dst->array, card, src->array, + (uint64_t)src->cardinality); +} + +/* Negation across the entire range of the container + * Compute the negation of src and write the result + * to *dst. A true return value indicates a bitset result, + * otherwise the result is an array container. + * We assume that dst is not pre-allocated. In + * case of failure, *dst will be NULL. + */ +bool bitset_container_negation(const bitset_container_t *src, void **dst) { + return bitset_container_negation_range(src, 0, (1 << 16), dst); +} + +/* inplace version */ +/* + * Same as bitset_container_negation except that if the output is to + * be a + * bitset_container_t, then src is modified and no allocation is made. + * If the output is to be an array_container_t, then caller is responsible + * to free the container. + * In all cases, the result is in *dst. + */ +bool bitset_container_negation_inplace(bitset_container_t *src, void **dst) { + return bitset_container_negation_range_inplace(src, 0, (1 << 16), dst); +} + +/* Negation across the entire range of container + * Compute the negation of src and write the result + * to *dst. Return values are the *_TYPECODES as defined * in containers.h + * We assume that dst is not pre-allocated. In + * case of failure, *dst will be NULL. + */ +int run_container_negation(const run_container_t *src, void **dst) { + return run_container_negation_range(src, 0, (1 << 16), dst); +} + +/* + * Same as run_container_negation except that if the output is to + * be a + * run_container_t, and has the capacity to hold the result, + * then src is modified and no allocation is made. + * In all cases, the result is in *dst. + */ +int run_container_negation_inplace(run_container_t *src, void **dst) { + return run_container_negation_range_inplace(src, 0, (1 << 16), dst); +} + +/* Negation across a range of the container. + * Compute the negation of src and write the result + * to *dst. Returns true if the result is a bitset container + * and false for an array container. *dst is not preallocated. + */ +bool array_container_negation_range(const array_container_t *src, + const int range_start, const int range_end, + void **dst) { + /* close port of the Java implementation */ + if (range_start >= range_end) { + *dst = array_container_clone(src); + return false; + } + + int32_t start_index = + binarySearch(src->array, src->cardinality, (uint16_t)range_start); + if (start_index < 0) start_index = -start_index - 1; + + int32_t last_index = + binarySearch(src->array, src->cardinality, (uint16_t)(range_end - 1)); + if (last_index < 0) last_index = -last_index - 2; + + const int32_t current_values_in_range = last_index - start_index + 1; + const int32_t span_to_be_flipped = range_end - range_start; + const int32_t new_values_in_range = + span_to_be_flipped - current_values_in_range; + const int32_t cardinality_change = + new_values_in_range - current_values_in_range; + const int32_t new_cardinality = src->cardinality + cardinality_change; + + if (new_cardinality > DEFAULT_MAX_SIZE) { + bitset_container_t *temp = bitset_container_from_array(src); + bitset_flip_range(temp->array, (uint32_t)range_start, + (uint32_t)range_end); + temp->cardinality = new_cardinality; + *dst = temp; + return true; + } + + array_container_t *arr = + array_container_create_given_capacity(new_cardinality); + *dst = (void *)arr; + if(new_cardinality == 0) { + arr->cardinality = new_cardinality; + return false; // we are done. + } + // copy stuff before the active area + memcpy(arr->array, src->array, start_index * sizeof(uint16_t)); + + // work on the range + int32_t out_pos = start_index, in_pos = start_index; + int32_t val_in_range = range_start; + for (; val_in_range < range_end && in_pos <= last_index; ++val_in_range) { + if ((uint16_t)val_in_range != src->array[in_pos]) { + arr->array[out_pos++] = (uint16_t)val_in_range; + } else { + ++in_pos; + } + } + for (; val_in_range < range_end; ++val_in_range) + arr->array[out_pos++] = (uint16_t)val_in_range; + + // content after the active range + memcpy(arr->array + out_pos, src->array + (last_index + 1), + (src->cardinality - (last_index + 1)) * sizeof(uint16_t)); + arr->cardinality = new_cardinality; + return false; +} + +/* Even when the result would fit, it is unclear how to make an + * inplace version without inefficient copying. + */ + +bool array_container_negation_range_inplace(array_container_t *src, + const int range_start, + const int range_end, void **dst) { + bool ans = array_container_negation_range(src, range_start, range_end, dst); + // TODO : try a real inplace version + array_container_free(src); + return ans; +} + +/* Negation across a range of the container + * Compute the negation of src and write the result + * to *dst. A true return value indicates a bitset result, + * otherwise the result is an array container. + * We assume that dst is not pre-allocated. In + * case of failure, *dst will be NULL. + */ +bool bitset_container_negation_range(const bitset_container_t *src, + const int range_start, const int range_end, + void **dst) { + // TODO maybe consider density-based estimate + // and sometimes build result directly as array, with + // conversion back to bitset if wrong. Or determine + // actual result cardinality, then go directly for the known final cont. + + // keep computation using bitsets as long as possible. + bitset_container_t *t = bitset_container_clone(src); + bitset_flip_range(t->array, (uint32_t)range_start, (uint32_t)range_end); + t->cardinality = bitset_container_compute_cardinality(t); + + if (t->cardinality > DEFAULT_MAX_SIZE) { + *dst = t; + return true; + } else { + *dst = array_container_from_bitset(t); + bitset_container_free(t); + return false; + } +} + +/* inplace version */ +/* + * Same as bitset_container_negation except that if the output is to + * be a + * bitset_container_t, then src is modified and no allocation is made. + * If the output is to be an array_container_t, then caller is responsible + * to free the container. + * In all cases, the result is in *dst. + */ +bool bitset_container_negation_range_inplace(bitset_container_t *src, + const int range_start, + const int range_end, void **dst) { + bitset_flip_range(src->array, (uint32_t)range_start, (uint32_t)range_end); + src->cardinality = bitset_container_compute_cardinality(src); + if (src->cardinality > DEFAULT_MAX_SIZE) { + *dst = src; + return true; + } + *dst = array_container_from_bitset(src); + bitset_container_free(src); + return false; +} + +/* Negation across a range of container + * Compute the negation of src and write the result + * to *dst. Return values are the *_TYPECODES as defined * in containers.h + * We assume that dst is not pre-allocated. In + * case of failure, *dst will be NULL. + */ +int run_container_negation_range(const run_container_t *src, + const int range_start, const int range_end, + void **dst) { + uint8_t return_typecode; + + // follows the Java implementation + if (range_end <= range_start) { + *dst = run_container_clone(src); + return RUN_CONTAINER_TYPE_CODE; + } + + run_container_t *ans = run_container_create_given_capacity( + src->n_runs + 1); // src->n_runs + 1); + int k = 0; + for (; k < src->n_runs && src->runs[k].value < range_start; ++k) { + ans->runs[k] = src->runs[k]; + ans->n_runs++; + } + + run_container_smart_append_exclusive( + ans, (uint16_t)range_start, (uint16_t)(range_end - range_start - 1)); + + for (; k < src->n_runs; ++k) { + run_container_smart_append_exclusive(ans, src->runs[k].value, + src->runs[k].length); + } + + *dst = convert_run_to_efficient_container(ans, &return_typecode); + if (return_typecode != RUN_CONTAINER_TYPE_CODE) run_container_free(ans); + + return return_typecode; +} + +/* + * Same as run_container_negation except that if the output is to + * be a + * run_container_t, and has the capacity to hold the result, + * then src is modified and no allocation is made. + * In all cases, the result is in *dst. + */ +int run_container_negation_range_inplace(run_container_t *src, + const int range_start, + const int range_end, void **dst) { + uint8_t return_typecode; + + if (range_end <= range_start) { + *dst = src; + return RUN_CONTAINER_TYPE_CODE; + } + + // TODO: efficient special case when range is 0 to 65535 inclusive + + if (src->capacity == src->n_runs) { + // no excess room. More checking to see if result can fit + bool last_val_before_range = false; + bool first_val_in_range = false; + bool last_val_in_range = false; + bool first_val_past_range = false; + + if (range_start > 0) + last_val_before_range = + run_container_contains(src, (uint16_t)(range_start - 1)); + first_val_in_range = run_container_contains(src, (uint16_t)range_start); + + if (last_val_before_range == first_val_in_range) { + last_val_in_range = + run_container_contains(src, (uint16_t)(range_end - 1)); + if (range_end != 0x10000) + first_val_past_range = + run_container_contains(src, (uint16_t)range_end); + + if (last_val_in_range == + first_val_past_range) { // no space for inplace + int ans = run_container_negation_range(src, range_start, + range_end, dst); + run_container_free(src); + return ans; + } + } + } + // all other cases: result will fit + + run_container_t *ans = src; + int my_nbr_runs = src->n_runs; + + ans->n_runs = 0; + int k = 0; + for (; (k < my_nbr_runs) && (src->runs[k].value < range_start); ++k) { + // ans->runs[k] = src->runs[k]; (would be self-copy) + ans->n_runs++; + } + + // as with Java implementation, use locals to give self a buffer of depth 1 + rle16_t buffered = (rle16_t){.value = (uint16_t)0, .length = (uint16_t)0}; + rle16_t next = buffered; + if (k < my_nbr_runs) buffered = src->runs[k]; + + run_container_smart_append_exclusive( + ans, (uint16_t)range_start, (uint16_t)(range_end - range_start - 1)); + + for (; k < my_nbr_runs; ++k) { + if (k + 1 < my_nbr_runs) next = src->runs[k + 1]; + + run_container_smart_append_exclusive(ans, buffered.value, + buffered.length); + buffered = next; + } + + *dst = convert_run_to_efficient_container(ans, &return_typecode); + if (return_typecode != RUN_CONTAINER_TYPE_CODE) run_container_free(ans); + + return return_typecode; +} +/* end file src/containers/mixed_negation.c */ +/* begin file src/containers/mixed_subset.c */ + +bool array_container_is_subset_bitset(const array_container_t* container1, + const bitset_container_t* container2) { + if (container2->cardinality != BITSET_UNKNOWN_CARDINALITY) { + if (container2->cardinality < container1->cardinality) { + return false; + } + } + for (int i = 0; i < container1->cardinality; ++i) { + if (!bitset_container_contains(container2, container1->array[i])) { + return false; + } + } + return true; +} + +bool run_container_is_subset_array(const run_container_t* container1, + const array_container_t* container2) { + if (run_container_cardinality(container1) > container2->cardinality) + return false; + int32_t start_pos = -1, stop_pos = -1; + for (int i = 0; i < container1->n_runs; ++i) { + int32_t start = container1->runs[i].value; + int32_t stop = start + container1->runs[i].length; + start_pos = advanceUntil(container2->array, stop_pos, + container2->cardinality, start); + stop_pos = advanceUntil(container2->array, stop_pos, + container2->cardinality, stop); + if (start_pos == container2->cardinality) { + return false; + } else if (stop_pos - start_pos != stop - start || + container2->array[start_pos] != start || + container2->array[stop_pos] != stop) { + return false; + } + } + return true; +} + +bool array_container_is_subset_run(const array_container_t* container1, + const run_container_t* container2) { + if (container1->cardinality > run_container_cardinality(container2)) + return false; + int i_array = 0, i_run = 0; + while (i_array < container1->cardinality && i_run < container2->n_runs) { + uint32_t start = container2->runs[i_run].value; + uint32_t stop = start + container2->runs[i_run].length; + if (container1->array[i_array] < start) { + return false; + } else if (container1->array[i_array] > stop) { + i_run++; + } else { // the value of the array is in the run + i_array++; + } + } + if (i_array == container1->cardinality) { + return true; + } else { + return false; + } +} + +bool run_container_is_subset_bitset(const run_container_t* container1, + const bitset_container_t* container2) { + // todo: this code could be much faster + if (container2->cardinality != BITSET_UNKNOWN_CARDINALITY) { + if (container2->cardinality < run_container_cardinality(container1)) { + return false; + } + } else { + int32_t card = bitset_container_compute_cardinality( + container2); // modify container2? + if (card < run_container_cardinality(container1)) { + return false; + } + } + for (int i = 0; i < container1->n_runs; ++i) { + uint32_t run_start = container1->runs[i].value; + uint32_t le = container1->runs[i].length; + for (uint32_t j = run_start; j <= run_start + le; ++j) { + if (!bitset_container_contains(container2, j)) { + return false; + } + } + } + return true; +} + +bool bitset_container_is_subset_run(const bitset_container_t* container1, + const run_container_t* container2) { + // todo: this code could be much faster + if (container1->cardinality != BITSET_UNKNOWN_CARDINALITY) { + if (container1->cardinality > run_container_cardinality(container2)) { + return false; + } + } + int32_t i_bitset = 0, i_run = 0; + while (i_bitset < BITSET_CONTAINER_SIZE_IN_WORDS && + i_run < container2->n_runs) { + uint64_t w = container1->array[i_bitset]; + while (w != 0 && i_run < container2->n_runs) { + uint32_t start = container2->runs[i_run].value; + uint32_t stop = start + container2->runs[i_run].length; + uint64_t t = w & (~w + 1); + uint16_t r = i_bitset * 64 + __builtin_ctzll(w); + if (r < start) { + return false; + } else if (r > stop) { + i_run++; + continue; + } else { + w ^= t; + } + } + if (w == 0) { + i_bitset++; + } else { + return false; + } + } + if (i_bitset < BITSET_CONTAINER_SIZE_IN_WORDS) { + // terminated iterating on the run containers, check that rest of bitset + // is empty + for (; i_bitset < BITSET_CONTAINER_SIZE_IN_WORDS; i_bitset++) { + if (container1->array[i_bitset] != 0) { + return false; + } + } + } + return true; +} +/* end file src/containers/mixed_subset.c */ +/* begin file src/containers/mixed_union.c */ +/* + * mixed_union.c + * + */ + +#include +#include + + +/* Compute the union of src_1 and src_2 and write the result to + * dst. */ +void array_bitset_container_union(const array_container_t *src_1, + const bitset_container_t *src_2, + bitset_container_t *dst) { + if (src_2 != dst) bitset_container_copy(src_2, dst); + dst->cardinality = (int32_t)bitset_set_list_withcard( + dst->array, dst->cardinality, src_1->array, src_1->cardinality); +} + +/* Compute the union of src_1 and src_2 and write the result to + * dst. It is allowed for src_2 to be dst. This version does not + * update the cardinality of dst (it is set to BITSET_UNKNOWN_CARDINALITY). */ +void array_bitset_container_lazy_union(const array_container_t *src_1, + const bitset_container_t *src_2, + bitset_container_t *dst) { + if (src_2 != dst) bitset_container_copy(src_2, dst); + bitset_set_list(dst->array, src_1->array, src_1->cardinality); + dst->cardinality = BITSET_UNKNOWN_CARDINALITY; +} + +void run_bitset_container_union(const run_container_t *src_1, + const bitset_container_t *src_2, + bitset_container_t *dst) { + assert(!run_container_is_full(src_1)); // catch this case upstream + if (src_2 != dst) bitset_container_copy(src_2, dst); + for (int32_t rlepos = 0; rlepos < src_1->n_runs; ++rlepos) { + rle16_t rle = src_1->runs[rlepos]; + bitset_set_lenrange(dst->array, rle.value, rle.length); + } + dst->cardinality = bitset_container_compute_cardinality(dst); +} + +void run_bitset_container_lazy_union(const run_container_t *src_1, + const bitset_container_t *src_2, + bitset_container_t *dst) { + assert(!run_container_is_full(src_1)); // catch this case upstream + if (src_2 != dst) bitset_container_copy(src_2, dst); + for (int32_t rlepos = 0; rlepos < src_1->n_runs; ++rlepos) { + rle16_t rle = src_1->runs[rlepos]; + bitset_set_lenrange(dst->array, rle.value, rle.length); + } + dst->cardinality = BITSET_UNKNOWN_CARDINALITY; +} + +// why do we leave the result as a run container?? +void array_run_container_union(const array_container_t *src_1, + const run_container_t *src_2, + run_container_t *dst) { + if (run_container_is_full(src_2)) { + run_container_copy(src_2, dst); + return; + } + // TODO: see whether the "2*" is spurious + run_container_grow(dst, 2 * (src_1->cardinality + src_2->n_runs), false); + int32_t rlepos = 0; + int32_t arraypos = 0; + rle16_t previousrle; + if (src_2->runs[rlepos].value <= src_1->array[arraypos]) { + previousrle = run_container_append_first(dst, src_2->runs[rlepos]); + rlepos++; + } else { + previousrle = + run_container_append_value_first(dst, src_1->array[arraypos]); + arraypos++; + } + while ((rlepos < src_2->n_runs) && (arraypos < src_1->cardinality)) { + if (src_2->runs[rlepos].value <= src_1->array[arraypos]) { + run_container_append(dst, src_2->runs[rlepos], &previousrle); + rlepos++; + } else { + run_container_append_value(dst, src_1->array[arraypos], + &previousrle); + arraypos++; + } + } + if (arraypos < src_1->cardinality) { + while (arraypos < src_1->cardinality) { + run_container_append_value(dst, src_1->array[arraypos], + &previousrle); + arraypos++; + } + } else { + while (rlepos < src_2->n_runs) { + run_container_append(dst, src_2->runs[rlepos], &previousrle); + rlepos++; + } + } +} + +void array_run_container_inplace_union(const array_container_t *src_1, + run_container_t *src_2) { + if (run_container_is_full(src_2)) { + return; + } + const int32_t maxoutput = src_1->cardinality + src_2->n_runs; + const int32_t neededcapacity = maxoutput + src_2->n_runs; + if (src_2->capacity < neededcapacity) + run_container_grow(src_2, neededcapacity, true); + memmove(src_2->runs + maxoutput, src_2->runs, + src_2->n_runs * sizeof(rle16_t)); + rle16_t *inputsrc2 = src_2->runs + maxoutput; + int32_t rlepos = 0; + int32_t arraypos = 0; + int src2nruns = src_2->n_runs; + src_2->n_runs = 0; + + rle16_t previousrle; + + if (inputsrc2[rlepos].value <= src_1->array[arraypos]) { + previousrle = run_container_append_first(src_2, inputsrc2[rlepos]); + rlepos++; + } else { + previousrle = + run_container_append_value_first(src_2, src_1->array[arraypos]); + arraypos++; + } + + while ((rlepos < src2nruns) && (arraypos < src_1->cardinality)) { + if (inputsrc2[rlepos].value <= src_1->array[arraypos]) { + run_container_append(src_2, inputsrc2[rlepos], &previousrle); + rlepos++; + } else { + run_container_append_value(src_2, src_1->array[arraypos], + &previousrle); + arraypos++; + } + } + if (arraypos < src_1->cardinality) { + while (arraypos < src_1->cardinality) { + run_container_append_value(src_2, src_1->array[arraypos], + &previousrle); + arraypos++; + } + } else { + while (rlepos < src2nruns) { + run_container_append(src_2, inputsrc2[rlepos], &previousrle); + rlepos++; + } + } +} + +bool array_array_container_union(const array_container_t *src_1, + const array_container_t *src_2, void **dst) { + int totalCardinality = src_1->cardinality + src_2->cardinality; + if (totalCardinality <= DEFAULT_MAX_SIZE) { + *dst = array_container_create_given_capacity(totalCardinality); + if (*dst != NULL) { + array_container_union(src_1, src_2, (array_container_t *)*dst); + } else { + return true; // otherwise failure won't be caught + } + return false; // not a bitset + } + *dst = bitset_container_create(); + bool returnval = true; // expect a bitset + if (*dst != NULL) { + bitset_container_t *ourbitset = (bitset_container_t *)*dst; + bitset_set_list(ourbitset->array, src_1->array, src_1->cardinality); + ourbitset->cardinality = (int32_t)bitset_set_list_withcard( + ourbitset->array, src_1->cardinality, src_2->array, + src_2->cardinality); + if (ourbitset->cardinality <= DEFAULT_MAX_SIZE) { + // need to convert! + *dst = array_container_from_bitset(ourbitset); + bitset_container_free(ourbitset); + returnval = false; // not going to be a bitset + } + } + return returnval; +} + +bool array_array_container_inplace_union(array_container_t *src_1, + const array_container_t *src_2, void **dst) { + int totalCardinality = src_1->cardinality + src_2->cardinality; + *dst = NULL; + if (totalCardinality <= DEFAULT_MAX_SIZE) { + if(src_1->capacity < totalCardinality) { + *dst = array_container_create_given_capacity(2 * totalCardinality); // be purposefully generous + if (*dst != NULL) { + array_container_union(src_1, src_2, (array_container_t *)*dst); + } else { + return true; // otherwise failure won't be caught + } + return false; // not a bitset + } else { + memmove(src_1->array + src_2->cardinality, src_1->array, src_1->cardinality * sizeof(uint16_t)); + src_1->cardinality = (int32_t)union_uint16(src_1->array + src_2->cardinality, src_1->cardinality, + src_2->array, src_2->cardinality, src_1->array); + return false; // not a bitset + } + } + *dst = bitset_container_create(); + bool returnval = true; // expect a bitset + if (*dst != NULL) { + bitset_container_t *ourbitset = (bitset_container_t *)*dst; + bitset_set_list(ourbitset->array, src_1->array, src_1->cardinality); + ourbitset->cardinality = (int32_t)bitset_set_list_withcard( + ourbitset->array, src_1->cardinality, src_2->array, + src_2->cardinality); + if (ourbitset->cardinality <= DEFAULT_MAX_SIZE) { + // need to convert! + if(src_1->capacity < ourbitset->cardinality) { + array_container_grow(src_1, ourbitset->cardinality, false); + } + + bitset_extract_setbits_uint16(ourbitset->array, BITSET_CONTAINER_SIZE_IN_WORDS, + src_1->array, 0); + src_1->cardinality = ourbitset->cardinality; + *dst = src_1; + bitset_container_free(ourbitset); + returnval = false; // not going to be a bitset + } + } + return returnval; +} + + +bool array_array_container_lazy_union(const array_container_t *src_1, + const array_container_t *src_2, + void **dst) { + int totalCardinality = src_1->cardinality + src_2->cardinality; + if (totalCardinality <= ARRAY_LAZY_LOWERBOUND) { + *dst = array_container_create_given_capacity(totalCardinality); + if (*dst != NULL) { + array_container_union(src_1, src_2, (array_container_t *)*dst); + } else { + return true; // otherwise failure won't be caught + } + return false; // not a bitset + } + *dst = bitset_container_create(); + bool returnval = true; // expect a bitset + if (*dst != NULL) { + bitset_container_t *ourbitset = (bitset_container_t *)*dst; + bitset_set_list(ourbitset->array, src_1->array, src_1->cardinality); + bitset_set_list(ourbitset->array, src_2->array, src_2->cardinality); + ourbitset->cardinality = BITSET_UNKNOWN_CARDINALITY; + } + return returnval; +} + + +bool array_array_container_lazy_inplace_union(array_container_t *src_1, + const array_container_t *src_2, + void **dst) { + int totalCardinality = src_1->cardinality + src_2->cardinality; + *dst = NULL; + if (totalCardinality <= ARRAY_LAZY_LOWERBOUND) { + if(src_1->capacity < totalCardinality) { + *dst = array_container_create_given_capacity(2 * totalCardinality); // be purposefully generous + if (*dst != NULL) { + array_container_union(src_1, src_2, (array_container_t *)*dst); + } else { + return true; // otherwise failure won't be caught + } + return false; // not a bitset + } else { + memmove(src_1->array + src_2->cardinality, src_1->array, src_1->cardinality * sizeof(uint16_t)); + src_1->cardinality = (int32_t)union_uint16(src_1->array + src_2->cardinality, src_1->cardinality, + src_2->array, src_2->cardinality, src_1->array); + return false; // not a bitset + } + } + *dst = bitset_container_create(); + bool returnval = true; // expect a bitset + if (*dst != NULL) { + bitset_container_t *ourbitset = (bitset_container_t *)*dst; + bitset_set_list(ourbitset->array, src_1->array, src_1->cardinality); + bitset_set_list(ourbitset->array, src_2->array, src_2->cardinality); + ourbitset->cardinality = BITSET_UNKNOWN_CARDINALITY; + } + return returnval; +} +/* end file src/containers/mixed_union.c */ +/* begin file src/containers/mixed_xor.c */ +/* + * mixed_xor.c + */ + +#include +#include + + +/* Compute the xor of src_1 and src_2 and write the result to + * dst (which has no container initially). + * Result is true iff dst is a bitset */ +bool array_bitset_container_xor(const array_container_t *src_1, + const bitset_container_t *src_2, void **dst) { + bitset_container_t *result = bitset_container_create(); + bitset_container_copy(src_2, result); + result->cardinality = (int32_t)bitset_flip_list_withcard( + result->array, result->cardinality, src_1->array, src_1->cardinality); + + // do required type conversions. + if (result->cardinality <= DEFAULT_MAX_SIZE) { + *dst = array_container_from_bitset(result); + bitset_container_free(result); + return false; // not bitset + } + *dst = result; + return true; // bitset +} + +/* Compute the xor of src_1 and src_2 and write the result to + * dst. It is allowed for src_2 to be dst. This version does not + * update the cardinality of dst (it is set to BITSET_UNKNOWN_CARDINALITY). + */ + +void array_bitset_container_lazy_xor(const array_container_t *src_1, + const bitset_container_t *src_2, + bitset_container_t *dst) { + if (src_2 != dst) bitset_container_copy(src_2, dst); + bitset_flip_list(dst->array, src_1->array, src_1->cardinality); + dst->cardinality = BITSET_UNKNOWN_CARDINALITY; +} + +/* Compute the xor of src_1 and src_2 and write the result to + * dst. Result may be either a bitset or an array container + * (returns "result is bitset"). dst does not initially have + * any container, but becomes either a bitset container (return + * result true) or an array container. + */ + +bool run_bitset_container_xor(const run_container_t *src_1, + const bitset_container_t *src_2, void **dst) { + bitset_container_t *result = bitset_container_create(); + + bitset_container_copy(src_2, result); + for (int32_t rlepos = 0; rlepos < src_1->n_runs; ++rlepos) { + rle16_t rle = src_1->runs[rlepos]; + bitset_flip_range(result->array, rle.value, + rle.value + rle.length + UINT32_C(1)); + } + result->cardinality = bitset_container_compute_cardinality(result); + + if (result->cardinality <= DEFAULT_MAX_SIZE) { + *dst = array_container_from_bitset(result); + bitset_container_free(result); + return false; // not bitset + } + *dst = result; + return true; // bitset +} + +/* lazy xor. Dst is initialized and may be equal to src_2. + * Result is left as a bitset container, even if actual + * cardinality would dictate an array container. + */ + +void run_bitset_container_lazy_xor(const run_container_t *src_1, + const bitset_container_t *src_2, + bitset_container_t *dst) { + if (src_2 != dst) bitset_container_copy(src_2, dst); + for (int32_t rlepos = 0; rlepos < src_1->n_runs; ++rlepos) { + rle16_t rle = src_1->runs[rlepos]; + bitset_flip_range(dst->array, rle.value, + rle.value + rle.length + UINT32_C(1)); + } + dst->cardinality = BITSET_UNKNOWN_CARDINALITY; +} + +/* dst does not indicate a valid container initially. Eventually it + * can become any kind of container. + */ + +int array_run_container_xor(const array_container_t *src_1, + const run_container_t *src_2, void **dst) { + // semi following Java XOR implementation as of May 2016 + // the C OR implementation works quite differently and can return a run + // container + // TODO could optimize for full run containers. + + // use of lazy following Java impl. + const int arbitrary_threshold = 32; + if (src_1->cardinality < arbitrary_threshold) { + run_container_t *ans = run_container_create(); + array_run_container_lazy_xor(src_1, src_2, ans); // keeps runs. + uint8_t typecode_after; + *dst = + convert_run_to_efficient_container_and_free(ans, &typecode_after); + return typecode_after; + } + + int card = run_container_cardinality(src_2); + if (card <= DEFAULT_MAX_SIZE) { + // Java implementation works with the array, xoring the run elements via + // iterator + array_container_t *temp = array_container_from_run(src_2); + bool ret_is_bitset = array_array_container_xor(temp, src_1, dst); + array_container_free(temp); + return ret_is_bitset ? BITSET_CONTAINER_TYPE_CODE + : ARRAY_CONTAINER_TYPE_CODE; + + } else { // guess that it will end up as a bitset + bitset_container_t *result = bitset_container_from_run(src_2); + bool is_bitset = bitset_array_container_ixor(result, src_1, dst); + // any necessary type conversion has been done by the ixor + int retval = (is_bitset ? BITSET_CONTAINER_TYPE_CODE + : ARRAY_CONTAINER_TYPE_CODE); + return retval; + } +} + +/* Dst is a valid run container. (Can it be src_2? Let's say not.) + * Leaves result as run container, even if other options are + * smaller. + */ + +void array_run_container_lazy_xor(const array_container_t *src_1, + const run_container_t *src_2, + run_container_t *dst) { + run_container_grow(dst, src_1->cardinality + src_2->n_runs, false); + int32_t rlepos = 0; + int32_t arraypos = 0; + dst->n_runs = 0; + + while ((rlepos < src_2->n_runs) && (arraypos < src_1->cardinality)) { + if (src_2->runs[rlepos].value <= src_1->array[arraypos]) { + run_container_smart_append_exclusive(dst, src_2->runs[rlepos].value, + src_2->runs[rlepos].length); + rlepos++; + } else { + run_container_smart_append_exclusive(dst, src_1->array[arraypos], + 0); + arraypos++; + } + } + while (arraypos < src_1->cardinality) { + run_container_smart_append_exclusive(dst, src_1->array[arraypos], 0); + arraypos++; + } + while (rlepos < src_2->n_runs) { + run_container_smart_append_exclusive(dst, src_2->runs[rlepos].value, + src_2->runs[rlepos].length); + rlepos++; + } +} + +/* dst does not indicate a valid container initially. Eventually it + * can become any kind of container. + */ + +int run_run_container_xor(const run_container_t *src_1, + const run_container_t *src_2, void **dst) { + run_container_t *ans = run_container_create(); + run_container_xor(src_1, src_2, ans); + uint8_t typecode_after; + *dst = convert_run_to_efficient_container_and_free(ans, &typecode_after); + return typecode_after; +} + +/* + * Java implementation (as of May 2016) for array_run, run_run + * and bitset_run don't do anything different for inplace. + * Could adopt the mixed_union.c approach instead (ie, using + * smart_append_exclusive) + * + */ + +bool array_array_container_xor(const array_container_t *src_1, + const array_container_t *src_2, void **dst) { + int totalCardinality = + src_1->cardinality + src_2->cardinality; // upper bound + if (totalCardinality <= DEFAULT_MAX_SIZE) { + *dst = array_container_create_given_capacity(totalCardinality); + array_container_xor(src_1, src_2, (array_container_t *)*dst); + return false; // not a bitset + } + *dst = bitset_container_from_array(src_1); + bool returnval = true; // expect a bitset + bitset_container_t *ourbitset = (bitset_container_t *)*dst; + ourbitset->cardinality = (uint32_t)bitset_flip_list_withcard( + ourbitset->array, src_1->cardinality, src_2->array, src_2->cardinality); + if (ourbitset->cardinality <= DEFAULT_MAX_SIZE) { + // need to convert! + *dst = array_container_from_bitset(ourbitset); + bitset_container_free(ourbitset); + returnval = false; // not going to be a bitset + } + + return returnval; +} + +bool array_array_container_lazy_xor(const array_container_t *src_1, + const array_container_t *src_2, + void **dst) { + int totalCardinality = src_1->cardinality + src_2->cardinality; + // upper bound, but probably poor estimate for xor + if (totalCardinality <= ARRAY_LAZY_LOWERBOUND) { + *dst = array_container_create_given_capacity(totalCardinality); + if (*dst != NULL) + array_container_xor(src_1, src_2, (array_container_t *)*dst); + return false; // not a bitset + } + *dst = bitset_container_from_array(src_1); + bool returnval = true; // expect a bitset (maybe, for XOR??) + if (*dst != NULL) { + bitset_container_t *ourbitset = (bitset_container_t *)*dst; + bitset_flip_list(ourbitset->array, src_2->array, src_2->cardinality); + ourbitset->cardinality = BITSET_UNKNOWN_CARDINALITY; + } + return returnval; +} + +/* Compute the xor of src_1 and src_2 and write the result to + * dst (which has no container initially). Return value is + * "dst is a bitset" + */ + +bool bitset_bitset_container_xor(const bitset_container_t *src_1, + const bitset_container_t *src_2, void **dst) { + bitset_container_t *ans = bitset_container_create(); + int card = bitset_container_xor(src_1, src_2, ans); + if (card <= DEFAULT_MAX_SIZE) { + *dst = array_container_from_bitset(ans); + bitset_container_free(ans); + return false; // not bitset + } else { + *dst = ans; + return true; + } +} + +/* Compute the xor of src_1 and src_2 and write the result to + * dst (which has no container initially). It will modify src_1 + * to be dst if the result is a bitset. Otherwise, it will + * free src_1 and dst will be a new array container. In both + * cases, the caller is responsible for deallocating dst. + * Returns true iff dst is a bitset */ + +bool bitset_array_container_ixor(bitset_container_t *src_1, + const array_container_t *src_2, void **dst) { + *dst = src_1; + src_1->cardinality = (uint32_t)bitset_flip_list_withcard( + src_1->array, src_1->cardinality, src_2->array, src_2->cardinality); + + if (src_1->cardinality <= DEFAULT_MAX_SIZE) { + *dst = array_container_from_bitset(src_1); + bitset_container_free(src_1); + return false; // not bitset + } else + return true; +} + +/* a bunch of in-place, some of which may not *really* be inplace. + * TODO: write actual inplace routine if efficiency warrants it + * Anything inplace with a bitset is a good candidate + */ + +bool bitset_bitset_container_ixor(bitset_container_t *src_1, + const bitset_container_t *src_2, void **dst) { + bool ans = bitset_bitset_container_xor(src_1, src_2, dst); + bitset_container_free(src_1); + return ans; +} + +bool array_bitset_container_ixor(array_container_t *src_1, + const bitset_container_t *src_2, void **dst) { + bool ans = array_bitset_container_xor(src_1, src_2, dst); + array_container_free(src_1); + return ans; +} + +/* Compute the xor of src_1 and src_2 and write the result to + * dst. Result may be either a bitset or an array container + * (returns "result is bitset"). dst does not initially have + * any container, but becomes either a bitset container (return + * result true) or an array container. + */ + +bool run_bitset_container_ixor(run_container_t *src_1, + const bitset_container_t *src_2, void **dst) { + bool ans = run_bitset_container_xor(src_1, src_2, dst); + run_container_free(src_1); + return ans; +} + +bool bitset_run_container_ixor(bitset_container_t *src_1, + const run_container_t *src_2, void **dst) { + bool ans = run_bitset_container_xor(src_2, src_1, dst); + bitset_container_free(src_1); + return ans; +} + +/* dst does not indicate a valid container initially. Eventually it + * can become any kind of container. + */ + +int array_run_container_ixor(array_container_t *src_1, + const run_container_t *src_2, void **dst) { + int ans = array_run_container_xor(src_1, src_2, dst); + array_container_free(src_1); + return ans; +} + +int run_array_container_ixor(run_container_t *src_1, + const array_container_t *src_2, void **dst) { + int ans = array_run_container_xor(src_2, src_1, dst); + run_container_free(src_1); + return ans; +} + +bool array_array_container_ixor(array_container_t *src_1, + const array_container_t *src_2, void **dst) { + bool ans = array_array_container_xor(src_1, src_2, dst); + array_container_free(src_1); + return ans; +} + +int run_run_container_ixor(run_container_t *src_1, const run_container_t *src_2, + void **dst) { + int ans = run_run_container_xor(src_1, src_2, dst); + run_container_free(src_1); + return ans; +} +/* end file src/containers/mixed_xor.c */ +/* begin file src/containers/run.c */ +#include +#include + + +bool run_container_add(run_container_t *run, uint16_t pos) { + int32_t index = interleavedBinarySearch(run->runs, run->n_runs, pos); + if (index >= 0) return false; // already there + index = -index - 2; // points to preceding value, possibly -1 + if (index >= 0) { // possible match + int32_t offset = pos - run->runs[index].value; + int32_t le = run->runs[index].length; + if (offset <= le) return false; // already there + if (offset == le + 1) { + // we may need to fuse + if (index + 1 < run->n_runs) { + if (run->runs[index + 1].value == pos + 1) { + // indeed fusion is needed + run->runs[index].length = run->runs[index + 1].value + + run->runs[index + 1].length - + run->runs[index].value; + recoverRoomAtIndex(run, (uint16_t)(index + 1)); + return true; + } + } + run->runs[index].length++; + return true; + } + if (index + 1 < run->n_runs) { + // we may need to fuse + if (run->runs[index + 1].value == pos + 1) { + // indeed fusion is needed + run->runs[index + 1].value = pos; + run->runs[index + 1].length = run->runs[index + 1].length + 1; + return true; + } + } + } + if (index == -1) { + // we may need to extend the first run + if (0 < run->n_runs) { + if (run->runs[0].value == pos + 1) { + run->runs[0].length++; + run->runs[0].value--; + return true; + } + } + } + makeRoomAtIndex(run, (uint16_t)(index + 1)); + run->runs[index + 1].value = pos; + run->runs[index + 1].length = 0; + return true; +} + +/* Create a new run container. Return NULL in case of failure. */ +run_container_t *run_container_create_given_capacity(int32_t size) { + run_container_t *run; + /* Allocate the run container itself. */ + run = (run_container_t *)malloc(sizeof(run_container_t)); + assert (run); + if (size <= 0) // we don't want to rely on malloc(0) + run->runs = NULL; + run->runs = (rle16_t *)malloc(sizeof(rle16_t) * size); + assert (run->runs); + run->capacity = size; + run->n_runs = 0; + return run; +} + +int run_container_shrink_to_fit(run_container_t *src) { + if (src->n_runs == src->capacity) return 0; // nothing to do + int savings = src->capacity - src->n_runs; + src->capacity = src->n_runs; + rle16_t *oldruns = src->runs; + src->runs = (rle16_t *)realloc(oldruns, src->capacity * sizeof(rle16_t)); + return savings; +} +/* Create a new run container. Return NULL in case of failure. */ +run_container_t *run_container_create(void) { + return run_container_create_given_capacity(RUN_DEFAULT_INIT_SIZE); +} + +run_container_t *run_container_clone(const run_container_t *src) { + run_container_t *run = run_container_create_given_capacity(src->capacity); + if (run == NULL) return NULL; + run->capacity = src->capacity; + run->n_runs = src->n_runs; + memcpy(run->runs, src->runs, src->n_runs * sizeof(rle16_t)); + return run; +} + +/* Free memory. */ +void run_container_free(run_container_t *run) { + if(run->runs != NULL) {// Jon Strabala reports that some tools complain otherwise + free(run->runs); + run->runs = NULL; // pedantic + } + free(run); +} + +void run_container_grow(run_container_t *run, int32_t min, bool copy) { + int32_t newCapacity = + (run->capacity == 0) + ? RUN_DEFAULT_INIT_SIZE + : run->capacity < 64 ? run->capacity * 2 + : run->capacity < 1024 ? run->capacity * 3 / 2 + : run->capacity * 5 / 4; + if (newCapacity < min) newCapacity = min; + run->capacity = newCapacity; + assert(run->capacity >= min); + if (copy) { + rle16_t *oldruns = run->runs; + run->runs = + (rle16_t *)realloc(oldruns, run->capacity * sizeof(rle16_t)); + } else { + // Jon Strabala reports that some tools complain otherwise + if (run->runs != NULL) { + free(run->runs); + } + run->runs = (rle16_t *)malloc(run->capacity * sizeof(rle16_t)); + } + // handle the case where realloc fails + if (run->runs == NULL) { + fprintf(stderr, "could not allocate memory\n"); + } + assert(run->runs != NULL); +} + +/* copy one container into another */ +void run_container_copy(const run_container_t *src, run_container_t *dst) { + const int32_t n_runs = src->n_runs; + if (src->n_runs > dst->capacity) { + run_container_grow(dst, n_runs, false); + } + dst->n_runs = n_runs; + memcpy(dst->runs, src->runs, sizeof(rle16_t) * n_runs); +} + +/* Compute the union of `src_1' and `src_2' and write the result to `dst' + * It is assumed that `dst' is distinct from both `src_1' and `src_2'. */ +void run_container_union(const run_container_t *src_1, + const run_container_t *src_2, run_container_t *dst) { + // TODO: this could be a lot more efficient + + // we start out with inexpensive checks + const bool if1 = run_container_is_full(src_1); + const bool if2 = run_container_is_full(src_2); + if (if1 || if2) { + if (if1) { + run_container_copy(src_1, dst); + return; + } + if (if2) { + run_container_copy(src_2, dst); + return; + } + } + const int32_t neededcapacity = src_1->n_runs + src_2->n_runs; + if (dst->capacity < neededcapacity) + run_container_grow(dst, neededcapacity, false); + dst->n_runs = 0; + int32_t rlepos = 0; + int32_t xrlepos = 0; + + rle16_t previousrle; + if (src_1->runs[rlepos].value <= src_2->runs[xrlepos].value) { + previousrle = run_container_append_first(dst, src_1->runs[rlepos]); + rlepos++; + } else { + previousrle = run_container_append_first(dst, src_2->runs[xrlepos]); + xrlepos++; + } + + while ((xrlepos < src_2->n_runs) && (rlepos < src_1->n_runs)) { + rle16_t newrl; + if (src_1->runs[rlepos].value <= src_2->runs[xrlepos].value) { + newrl = src_1->runs[rlepos]; + rlepos++; + } else { + newrl = src_2->runs[xrlepos]; + xrlepos++; + } + run_container_append(dst, newrl, &previousrle); + } + while (xrlepos < src_2->n_runs) { + run_container_append(dst, src_2->runs[xrlepos], &previousrle); + xrlepos++; + } + while (rlepos < src_1->n_runs) { + run_container_append(dst, src_1->runs[rlepos], &previousrle); + rlepos++; + } +} + +/* Compute the union of `src_1' and `src_2' and write the result to `src_1' + */ +void run_container_union_inplace(run_container_t *src_1, + const run_container_t *src_2) { + // TODO: this could be a lot more efficient + + // we start out with inexpensive checks + const bool if1 = run_container_is_full(src_1); + const bool if2 = run_container_is_full(src_2); + if (if1 || if2) { + if (if1) { + return; + } + if (if2) { + run_container_copy(src_2, src_1); + return; + } + } + // we move the data to the end of the current array + const int32_t maxoutput = src_1->n_runs + src_2->n_runs; + const int32_t neededcapacity = maxoutput + src_1->n_runs; + if (src_1->capacity < neededcapacity) + run_container_grow(src_1, neededcapacity, true); + memmove(src_1->runs + maxoutput, src_1->runs, + src_1->n_runs * sizeof(rle16_t)); + rle16_t *inputsrc1 = src_1->runs + maxoutput; + const int32_t input1nruns = src_1->n_runs; + src_1->n_runs = 0; + int32_t rlepos = 0; + int32_t xrlepos = 0; + + rle16_t previousrle; + if (inputsrc1[rlepos].value <= src_2->runs[xrlepos].value) { + previousrle = run_container_append_first(src_1, inputsrc1[rlepos]); + rlepos++; + } else { + previousrle = run_container_append_first(src_1, src_2->runs[xrlepos]); + xrlepos++; + } + while ((xrlepos < src_2->n_runs) && (rlepos < input1nruns)) { + rle16_t newrl; + if (inputsrc1[rlepos].value <= src_2->runs[xrlepos].value) { + newrl = inputsrc1[rlepos]; + rlepos++; + } else { + newrl = src_2->runs[xrlepos]; + xrlepos++; + } + run_container_append(src_1, newrl, &previousrle); + } + while (xrlepos < src_2->n_runs) { + run_container_append(src_1, src_2->runs[xrlepos], &previousrle); + xrlepos++; + } + while (rlepos < input1nruns) { + run_container_append(src_1, inputsrc1[rlepos], &previousrle); + rlepos++; + } +} + +/* Compute the symmetric difference of `src_1' and `src_2' and write the result + * to `dst' + * It is assumed that `dst' is distinct from both `src_1' and `src_2'. */ +void run_container_xor(const run_container_t *src_1, + const run_container_t *src_2, run_container_t *dst) { + // don't bother to convert xor with full range into negation + // since negation is implemented similarly + + const int32_t neededcapacity = src_1->n_runs + src_2->n_runs; + if (dst->capacity < neededcapacity) + run_container_grow(dst, neededcapacity, false); + + int32_t pos1 = 0; + int32_t pos2 = 0; + dst->n_runs = 0; + + while ((pos1 < src_1->n_runs) && (pos2 < src_2->n_runs)) { + if (src_1->runs[pos1].value <= src_2->runs[pos2].value) { + run_container_smart_append_exclusive(dst, src_1->runs[pos1].value, + src_1->runs[pos1].length); + pos1++; + } else { + run_container_smart_append_exclusive(dst, src_2->runs[pos2].value, + src_2->runs[pos2].length); + pos2++; + } + } + while (pos1 < src_1->n_runs) { + run_container_smart_append_exclusive(dst, src_1->runs[pos1].value, + src_1->runs[pos1].length); + pos1++; + } + + while (pos2 < src_2->n_runs) { + run_container_smart_append_exclusive(dst, src_2->runs[pos2].value, + src_2->runs[pos2].length); + pos2++; + } +} + +/* Compute the intersection of src_1 and src_2 and write the result to + * dst. It is assumed that dst is distinct from both src_1 and src_2. */ +void run_container_intersection(const run_container_t *src_1, + const run_container_t *src_2, + run_container_t *dst) { + const bool if1 = run_container_is_full(src_1); + const bool if2 = run_container_is_full(src_2); + if (if1 || if2) { + if (if1) { + run_container_copy(src_2, dst); + return; + } + if (if2) { + run_container_copy(src_1, dst); + return; + } + } + // TODO: this could be a lot more efficient, could use SIMD optimizations + const int32_t neededcapacity = src_1->n_runs + src_2->n_runs; + if (dst->capacity < neededcapacity) + run_container_grow(dst, neededcapacity, false); + dst->n_runs = 0; + int32_t rlepos = 0; + int32_t xrlepos = 0; + int32_t start = src_1->runs[rlepos].value; + int32_t end = start + src_1->runs[rlepos].length + 1; + int32_t xstart = src_2->runs[xrlepos].value; + int32_t xend = xstart + src_2->runs[xrlepos].length + 1; + while ((rlepos < src_1->n_runs) && (xrlepos < src_2->n_runs)) { + if (end <= xstart) { + ++rlepos; + if (rlepos < src_1->n_runs) { + start = src_1->runs[rlepos].value; + end = start + src_1->runs[rlepos].length + 1; + } + } else if (xend <= start) { + ++xrlepos; + if (xrlepos < src_2->n_runs) { + xstart = src_2->runs[xrlepos].value; + xend = xstart + src_2->runs[xrlepos].length + 1; + } + } else { // they overlap + const int32_t lateststart = start > xstart ? start : xstart; + int32_t earliestend; + if (end == xend) { // improbable + earliestend = end; + rlepos++; + xrlepos++; + if (rlepos < src_1->n_runs) { + start = src_1->runs[rlepos].value; + end = start + src_1->runs[rlepos].length + 1; + } + if (xrlepos < src_2->n_runs) { + xstart = src_2->runs[xrlepos].value; + xend = xstart + src_2->runs[xrlepos].length + 1; + } + } else if (end < xend) { + earliestend = end; + rlepos++; + if (rlepos < src_1->n_runs) { + start = src_1->runs[rlepos].value; + end = start + src_1->runs[rlepos].length + 1; + } + + } else { // end > xend + earliestend = xend; + xrlepos++; + if (xrlepos < src_2->n_runs) { + xstart = src_2->runs[xrlepos].value; + xend = xstart + src_2->runs[xrlepos].length + 1; + } + } + dst->runs[dst->n_runs].value = (uint16_t)lateststart; + dst->runs[dst->n_runs].length = + (uint16_t)(earliestend - lateststart - 1); + dst->n_runs++; + } + } +} + +/* Compute the size of the intersection of src_1 and src_2 . */ +int run_container_intersection_cardinality(const run_container_t *src_1, + const run_container_t *src_2) { + const bool if1 = run_container_is_full(src_1); + const bool if2 = run_container_is_full(src_2); + if (if1 || if2) { + if (if1) { + return run_container_cardinality(src_2); + } + if (if2) { + return run_container_cardinality(src_1); + } + } + int answer = 0; + int32_t rlepos = 0; + int32_t xrlepos = 0; + int32_t start = src_1->runs[rlepos].value; + int32_t end = start + src_1->runs[rlepos].length + 1; + int32_t xstart = src_2->runs[xrlepos].value; + int32_t xend = xstart + src_2->runs[xrlepos].length + 1; + while ((rlepos < src_1->n_runs) && (xrlepos < src_2->n_runs)) { + if (end <= xstart) { + ++rlepos; + if (rlepos < src_1->n_runs) { + start = src_1->runs[rlepos].value; + end = start + src_1->runs[rlepos].length + 1; + } + } else if (xend <= start) { + ++xrlepos; + if (xrlepos < src_2->n_runs) { + xstart = src_2->runs[xrlepos].value; + xend = xstart + src_2->runs[xrlepos].length + 1; + } + } else { // they overlap + const int32_t lateststart = start > xstart ? start : xstart; + int32_t earliestend; + if (end == xend) { // improbable + earliestend = end; + rlepos++; + xrlepos++; + if (rlepos < src_1->n_runs) { + start = src_1->runs[rlepos].value; + end = start + src_1->runs[rlepos].length + 1; + } + if (xrlepos < src_2->n_runs) { + xstart = src_2->runs[xrlepos].value; + xend = xstart + src_2->runs[xrlepos].length + 1; + } + } else if (end < xend) { + earliestend = end; + rlepos++; + if (rlepos < src_1->n_runs) { + start = src_1->runs[rlepos].value; + end = start + src_1->runs[rlepos].length + 1; + } + + } else { // end > xend + earliestend = xend; + xrlepos++; + if (xrlepos < src_2->n_runs) { + xstart = src_2->runs[xrlepos].value; + xend = xstart + src_2->runs[xrlepos].length + 1; + } + } + answer += earliestend - lateststart; + } + } + return answer; +} + +bool run_container_intersect(const run_container_t *src_1, + const run_container_t *src_2) { + const bool if1 = run_container_is_full(src_1); + const bool if2 = run_container_is_full(src_2); + if (if1 || if2) { + if (if1) { + return !run_container_empty(src_2); + } + if (if2) { + return !run_container_empty(src_1); + } + } + int32_t rlepos = 0; + int32_t xrlepos = 0; + int32_t start = src_1->runs[rlepos].value; + int32_t end = start + src_1->runs[rlepos].length + 1; + int32_t xstart = src_2->runs[xrlepos].value; + int32_t xend = xstart + src_2->runs[xrlepos].length + 1; + while ((rlepos < src_1->n_runs) && (xrlepos < src_2->n_runs)) { + if (end <= xstart) { + ++rlepos; + if (rlepos < src_1->n_runs) { + start = src_1->runs[rlepos].value; + end = start + src_1->runs[rlepos].length + 1; + } + } else if (xend <= start) { + ++xrlepos; + if (xrlepos < src_2->n_runs) { + xstart = src_2->runs[xrlepos].value; + xend = xstart + src_2->runs[xrlepos].length + 1; + } + } else { // they overlap + return true; + } + } + return false; +} + + +/* Compute the difference of src_1 and src_2 and write the result to + * dst. It is assumed that dst is distinct from both src_1 and src_2. */ +void run_container_andnot(const run_container_t *src_1, + const run_container_t *src_2, run_container_t *dst) { + // following Java implementation as of June 2016 + + if (dst->capacity < src_1->n_runs + src_2->n_runs) + run_container_grow(dst, src_1->n_runs + src_2->n_runs, false); + + dst->n_runs = 0; + + int rlepos1 = 0; + int rlepos2 = 0; + int32_t start = src_1->runs[rlepos1].value; + int32_t end = start + src_1->runs[rlepos1].length + 1; + int32_t start2 = src_2->runs[rlepos2].value; + int32_t end2 = start2 + src_2->runs[rlepos2].length + 1; + + while ((rlepos1 < src_1->n_runs) && (rlepos2 < src_2->n_runs)) { + if (end <= start2) { + // output the first run + dst->runs[dst->n_runs++] = + (rle16_t){.value = (uint16_t)start, + .length = (uint16_t)(end - start - 1)}; + rlepos1++; + if (rlepos1 < src_1->n_runs) { + start = src_1->runs[rlepos1].value; + end = start + src_1->runs[rlepos1].length + 1; + } + } else if (end2 <= start) { + // exit the second run + rlepos2++; + if (rlepos2 < src_2->n_runs) { + start2 = src_2->runs[rlepos2].value; + end2 = start2 + src_2->runs[rlepos2].length + 1; + } + } else { + if (start < start2) { + dst->runs[dst->n_runs++] = + (rle16_t){.value = (uint16_t)start, + .length = (uint16_t)(start2 - start - 1)}; + } + if (end2 < end) { + start = end2; + } else { + rlepos1++; + if (rlepos1 < src_1->n_runs) { + start = src_1->runs[rlepos1].value; + end = start + src_1->runs[rlepos1].length + 1; + } + } + } + } + if (rlepos1 < src_1->n_runs) { + dst->runs[dst->n_runs++] = (rle16_t){ + .value = (uint16_t)start, .length = (uint16_t)(end - start - 1)}; + rlepos1++; + if (rlepos1 < src_1->n_runs) { + memcpy(dst->runs + dst->n_runs, src_1->runs + rlepos1, + sizeof(rle16_t) * (src_1->n_runs - rlepos1)); + dst->n_runs += src_1->n_runs - rlepos1; + } + } +} + +int run_container_to_uint32_array(void *vout, const run_container_t *cont, + uint32_t base) { + int outpos = 0; + uint32_t *out = (uint32_t *)vout; + for (int i = 0; i < cont->n_runs; ++i) { + uint32_t run_start = base + cont->runs[i].value; + uint16_t le = cont->runs[i].length; + for (int j = 0; j <= le; ++j) { + uint32_t val = run_start + j; + memcpy(out + outpos, &val, + sizeof(uint32_t)); // should be compiled as a MOV on x64 + outpos++; + } + } + return outpos; +} + +/* + * Print this container using printf (useful for debugging). + */ +void run_container_printf(const run_container_t *cont) { + for (int i = 0; i < cont->n_runs; ++i) { + uint16_t run_start = cont->runs[i].value; + uint16_t le = cont->runs[i].length; + printf("[%d,%d]", run_start, run_start + le); + } +} + +/* + * Print this container using printf as a comma-separated list of 32-bit + * integers starting at base. + */ +void run_container_printf_as_uint32_array(const run_container_t *cont, + uint32_t base) { + if (cont->n_runs == 0) return; + { + uint32_t run_start = base + cont->runs[0].value; + uint16_t le = cont->runs[0].length; + printf("%u", run_start); + for (uint32_t j = 1; j <= le; ++j) printf(",%u", run_start + j); + } + for (int32_t i = 1; i < cont->n_runs; ++i) { + uint32_t run_start = base + cont->runs[i].value; + uint16_t le = cont->runs[i].length; + for (uint32_t j = 0; j <= le; ++j) printf(",%u", run_start + j); + } +} + +int32_t run_container_serialize(const run_container_t *container, char *buf) { + int32_t l, off; + + memcpy(buf, &container->n_runs, off = sizeof(container->n_runs)); + memcpy(&buf[off], &container->capacity, sizeof(container->capacity)); + off += sizeof(container->capacity); + + l = sizeof(rle16_t) * container->n_runs; + memcpy(&buf[off], container->runs, l); + return (off + l); +} + +int32_t run_container_write(const run_container_t *container, char *buf) { + memcpy(buf, &container->n_runs, sizeof(uint16_t)); + memcpy(buf + sizeof(uint16_t), container->runs, + container->n_runs * sizeof(rle16_t)); + return run_container_size_in_bytes(container); +} + +int32_t run_container_read(int32_t cardinality, run_container_t *container, + const char *buf) { + (void)cardinality; + memcpy(&container->n_runs, buf, sizeof(uint16_t)); + if (container->n_runs > container->capacity) + run_container_grow(container, container->n_runs, false); + if(container->n_runs > 0) { + memcpy(container->runs, buf + sizeof(uint16_t), + container->n_runs * sizeof(rle16_t)); + } + return run_container_size_in_bytes(container); +} + +uint32_t run_container_serialization_len(const run_container_t *container) { + return (sizeof(container->n_runs) + sizeof(container->capacity) + + sizeof(rle16_t) * container->n_runs); +} + +void *run_container_deserialize(const char *buf, size_t buf_len) { + run_container_t *ptr; + + if (buf_len < 8 /* n_runs + capacity */) + return (NULL); + else + buf_len -= 8; + + if ((ptr = (run_container_t *)malloc(sizeof(run_container_t))) != NULL) { + size_t len; + int32_t off; + + memcpy(&ptr->n_runs, buf, off = 4); + memcpy(&ptr->capacity, &buf[off], 4); + off += 4; + + len = sizeof(rle16_t) * ptr->n_runs; + + if (len != buf_len) { + free(ptr); + return (NULL); + } + + if ((ptr->runs = (rle16_t *)malloc(len)) == NULL) { + free(ptr); + return (NULL); + } + + memcpy(ptr->runs, &buf[off], len); + + /* Check if returned values are monotonically increasing */ + for (int32_t i = 0, j = 0; i < ptr->n_runs; i++) { + if (ptr->runs[i].value < j) { + free(ptr->runs); + free(ptr); + return (NULL); + } else + j = ptr->runs[i].value; + } + } + + return (ptr); +} + +bool run_container_iterate(const run_container_t *cont, uint32_t base, + roaring_iterator iterator, void *ptr) { + for (int i = 0; i < cont->n_runs; ++i) { + uint32_t run_start = base + cont->runs[i].value; + uint16_t le = cont->runs[i].length; + + for (int j = 0; j <= le; ++j) + if (!iterator(run_start + j, ptr)) return false; + } + return true; +} + +bool run_container_iterate64(const run_container_t *cont, uint32_t base, + roaring_iterator64 iterator, uint64_t high_bits, + void *ptr) { + for (int i = 0; i < cont->n_runs; ++i) { + uint32_t run_start = base + cont->runs[i].value; + uint16_t le = cont->runs[i].length; + + for (int j = 0; j <= le; ++j) + if (!iterator(high_bits | (uint64_t)(run_start + j), ptr)) + return false; + } + return true; +} + +bool run_container_is_subset(const run_container_t *container1, + const run_container_t *container2) { + int i1 = 0, i2 = 0; + while (i1 < container1->n_runs && i2 < container2->n_runs) { + int start1 = container1->runs[i1].value; + int stop1 = start1 + container1->runs[i1].length; + int start2 = container2->runs[i2].value; + int stop2 = start2 + container2->runs[i2].length; + if (start1 < start2) { + return false; + } else { // start1 >= start2 + if (stop1 < stop2) { + i1++; + } else if (stop1 == stop2) { + i1++; + i2++; + } else { // stop1 > stop2 + i2++; + } + } + } + if (i1 == container1->n_runs) { + return true; + } else { + return false; + } +} + +// TODO: write smart_append_exclusive version to match the overloaded 1 param +// Java version (or is it even used?) + +// follows the Java implementation closely +// length is the rle-value. Ie, run [10,12) uses a length value 1. +void run_container_smart_append_exclusive(run_container_t *src, + const uint16_t start, + const uint16_t length) { + int old_end; + rle16_t *last_run = src->n_runs ? src->runs + (src->n_runs - 1) : NULL; + rle16_t *appended_last_run = src->runs + src->n_runs; + + if (!src->n_runs || + (start > (old_end = last_run->value + last_run->length + 1))) { + *appended_last_run = (rle16_t){.value = start, .length = length}; + src->n_runs++; + return; + } + if (old_end == start) { + // we merge + last_run->length += (length + 1); + return; + } + int new_end = start + length + 1; + + if (start == last_run->value) { + // wipe out previous + if (new_end < old_end) { + *last_run = (rle16_t){.value = (uint16_t)new_end, + .length = (uint16_t)(old_end - new_end - 1)}; + return; + } else if (new_end > old_end) { + *last_run = (rle16_t){.value = (uint16_t)old_end, + .length = (uint16_t)(new_end - old_end - 1)}; + return; + } else { + src->n_runs--; + return; + } + } + last_run->length = start - last_run->value - 1; + if (new_end < old_end) { + *appended_last_run = + (rle16_t){.value = (uint16_t)new_end, + .length = (uint16_t)(old_end - new_end - 1)}; + src->n_runs++; + } else if (new_end > old_end) { + *appended_last_run = + (rle16_t){.value = (uint16_t)old_end, + .length = (uint16_t)(new_end - old_end - 1)}; + src->n_runs++; + } +} + +bool run_container_select(const run_container_t *container, + uint32_t *start_rank, uint32_t rank, + uint32_t *element) { + for (int i = 0; i < container->n_runs; i++) { + uint16_t length = container->runs[i].length; + if (rank <= *start_rank + length) { + uint16_t value = container->runs[i].value; + *element = value + rank - (*start_rank); + return true; + } else + *start_rank += length + 1; + } + return false; +} + +int run_container_rank(const run_container_t *container, uint16_t x) { + int sum = 0; + uint32_t x32 = x; + for (int i = 0; i < container->n_runs; i++) { + uint32_t startpoint = container->runs[i].value; + uint32_t length = container->runs[i].length; + uint32_t endpoint = length + startpoint; + if (x <= endpoint) { + if (x < startpoint) break; + return sum + (x32 - startpoint) + 1; + } else { + sum += length + 1; + } + } + return sum; +} +/* end file src/containers/run.c */ +/* begin file src/roaring.c */ +#include +#include +#include +#include +#include +#include + +static inline bool is_cow(const roaring_bitmap_t *r) { + return r->high_low_container.flags & ROARING_FLAG_COW; +} +static inline bool is_frozen(const roaring_bitmap_t *r) { + return r->high_low_container.flags & ROARING_FLAG_FROZEN; +} + +// this is like roaring_bitmap_add, but it populates pointer arguments in such a +// way +// that we can recover the container touched, which, in turn can be used to +// accelerate some functions (when you repeatedly need to add to the same +// container) +static inline void *containerptr_roaring_bitmap_add(roaring_bitmap_t *r, + uint32_t val, + uint8_t *typecode, + int *index) { + uint16_t hb = val >> 16; + const int i = ra_get_index(&r->high_low_container, hb); + if (i >= 0) { + ra_unshare_container_at_index(&r->high_low_container, i); + void *container = + ra_get_container_at_index(&r->high_low_container, i, typecode); + uint8_t newtypecode = *typecode; + void *container2 = + container_add(container, val & 0xFFFF, *typecode, &newtypecode); + *index = i; + if (container2 != container) { + container_free(container, *typecode); + ra_set_container_at_index(&r->high_low_container, i, container2, + newtypecode); + *typecode = newtypecode; + return container2; + } else { + return container; + } + } else { + array_container_t *newac = array_container_create(); + void *container = container_add(newac, val & 0xFFFF, + ARRAY_CONTAINER_TYPE_CODE, typecode); + // we could just assume that it stays an array container + ra_insert_new_key_value_at(&r->high_low_container, -i - 1, hb, + container, *typecode); + *index = -i - 1; + return container; + } +} + +roaring_bitmap_t *roaring_bitmap_create(void) { + roaring_bitmap_t *ans = + (roaring_bitmap_t *)malloc(sizeof(roaring_bitmap_t)); + if (!ans) { + return NULL; + } + ra_init(&ans->high_low_container); + return ans; +} + +roaring_bitmap_t *roaring_bitmap_create_with_capacity(uint32_t cap) { + roaring_bitmap_t *ans = + (roaring_bitmap_t *)malloc(sizeof(roaring_bitmap_t)); + if (!ans) { + return NULL; + } + bool is_ok = ra_init_with_capacity(&ans->high_low_container, cap); + if (!is_ok) { + free(ans); + return NULL; + } + return ans; +} + +void roaring_bitmap_add_many(roaring_bitmap_t *r, size_t n_args, + const uint32_t *vals) { + void *container = NULL; // hold value of last container touched + uint8_t typecode = 0; // typecode of last container touched + uint32_t prev = 0; // previous valued inserted + size_t i = 0; // index of value + int containerindex = 0; + if (n_args == 0) return; + uint32_t val; + memcpy(&val, vals + i, sizeof(val)); + container = + containerptr_roaring_bitmap_add(r, val, &typecode, &containerindex); + prev = val; + i++; + for (; i < n_args; i++) { + memcpy(&val, vals + i, sizeof(val)); + if (((prev ^ val) >> 16) == + 0) { // no need to seek the container, it is at hand + // because we already have the container at hand, we can do the + // insertion + // automatically, bypassing the roaring_bitmap_add call + uint8_t newtypecode = typecode; + void *container2 = + container_add(container, val & 0xFFFF, typecode, &newtypecode); + if (container2 != container) { // rare instance when we need to + // change the container type + container_free(container, typecode); + ra_set_container_at_index(&r->high_low_container, + containerindex, container2, + newtypecode); + typecode = newtypecode; + container = container2; + } + } else { + container = containerptr_roaring_bitmap_add(r, val, &typecode, + &containerindex); + } + prev = val; + } +} + +roaring_bitmap_t *roaring_bitmap_of_ptr(size_t n_args, const uint32_t *vals) { + roaring_bitmap_t *answer = roaring_bitmap_create(); + roaring_bitmap_add_many(answer, n_args, vals); + return answer; +} + +roaring_bitmap_t *roaring_bitmap_of(size_t n_args, ...) { + // todo: could be greatly optimized but we do not expect this call to ever + // include long lists + roaring_bitmap_t *answer = roaring_bitmap_create(); + va_list ap; + va_start(ap, n_args); + for (size_t i = 1; i <= n_args; i++) { + uint32_t val = va_arg(ap, uint32_t); + roaring_bitmap_add(answer, val); + } + va_end(ap); + return answer; +} + +static inline uint32_t minimum_uint32(uint32_t a, uint32_t b) { + return (a < b) ? a : b; +} + +static inline uint64_t minimum_uint64(uint64_t a, uint64_t b) { + return (a < b) ? a : b; +} + +roaring_bitmap_t *roaring_bitmap_from_range(uint64_t min, uint64_t max, + uint32_t step) { + if(max >= UINT64_C(0x100000000)) { + max = UINT64_C(0x100000000); + } + if (step == 0) return NULL; + if (max <= min) return NULL; + roaring_bitmap_t *answer = roaring_bitmap_create(); + if (step >= (1 << 16)) { + for (uint32_t value = (uint32_t)min; value < max; value += step) { + roaring_bitmap_add(answer, value); + } + return answer; + } + uint64_t min_tmp = min; + do { + uint32_t key = (uint32_t)min_tmp >> 16; + uint32_t container_min = min_tmp & 0xFFFF; + uint32_t container_max = (uint32_t)minimum_uint64(max - (key << 16), 1 << 16); + uint8_t type; + void *container = container_from_range(&type, container_min, + container_max, (uint16_t)step); + ra_append(&answer->high_low_container, key, container, type); + uint32_t gap = container_max - container_min + step - 1; + min_tmp += gap - (gap % step); + } while (min_tmp < max); + // cardinality of bitmap will be ((uint64_t) max - min + step - 1 ) / step + return answer; +} + +void roaring_bitmap_add_range_closed(roaring_bitmap_t *ra, uint32_t min, uint32_t max) { + if (min > max) { + return; + } + + uint32_t min_key = min >> 16; + uint32_t max_key = max >> 16; + + int32_t num_required_containers = max_key - min_key + 1; + int32_t suffix_length = count_greater(ra->high_low_container.keys, + ra->high_low_container.size, + max_key); + int32_t prefix_length = count_less(ra->high_low_container.keys, + ra->high_low_container.size - suffix_length, + min_key); + int32_t common_length = ra->high_low_container.size - prefix_length - suffix_length; + + if (num_required_containers > common_length) { + ra_shift_tail(&ra->high_low_container, suffix_length, + num_required_containers - common_length); + } + + int32_t src = prefix_length + common_length - 1; + int32_t dst = ra->high_low_container.size - suffix_length - 1; + for (uint32_t key = max_key; key != min_key-1; key--) { // beware of min_key==0 + uint32_t container_min = (min_key == key) ? (min & 0xffff) : 0; + uint32_t container_max = (max_key == key) ? (max & 0xffff) : 0xffff; + void* new_container; + uint8_t new_type; + + if (src >= 0 && ra->high_low_container.keys[src] == key) { + ra_unshare_container_at_index(&ra->high_low_container, src); + new_container = container_add_range(ra->high_low_container.containers[src], + ra->high_low_container.typecodes[src], + container_min, container_max, &new_type); + if (new_container != ra->high_low_container.containers[src]) { + container_free(ra->high_low_container.containers[src], + ra->high_low_container.typecodes[src]); + } + src--; + } else { + new_container = container_from_range(&new_type, container_min, + container_max+1, 1); + } + ra_replace_key_and_container_at_index(&ra->high_low_container, dst, + key, new_container, new_type); + dst--; + } +} + +void roaring_bitmap_remove_range_closed(roaring_bitmap_t *ra, uint32_t min, uint32_t max) { + if (min > max) { + return; + } + + uint32_t min_key = min >> 16; + uint32_t max_key = max >> 16; + + int32_t src = count_less(ra->high_low_container.keys, ra->high_low_container.size, min_key); + int32_t dst = src; + while (src < ra->high_low_container.size && ra->high_low_container.keys[src] <= max_key) { + uint32_t container_min = (min_key == ra->high_low_container.keys[src]) ? (min & 0xffff) : 0; + uint32_t container_max = (max_key == ra->high_low_container.keys[src]) ? (max & 0xffff) : 0xffff; + ra_unshare_container_at_index(&ra->high_low_container, src); + void *new_container; + uint8_t new_type; + new_container = container_remove_range(ra->high_low_container.containers[src], + ra->high_low_container.typecodes[src], + container_min, container_max, + &new_type); + if (new_container != ra->high_low_container.containers[src]) { + container_free(ra->high_low_container.containers[src], + ra->high_low_container.typecodes[src]); + } + if (new_container) { + ra_replace_key_and_container_at_index(&ra->high_low_container, dst, + ra->high_low_container.keys[src], + new_container, new_type); + dst++; + } + src++; + } + if (src > dst) { + ra_shift_tail(&ra->high_low_container, ra->high_low_container.size - src, dst - src); + } +} + +void roaring_bitmap_printf(const roaring_bitmap_t *ra) { + printf("{"); + for (int i = 0; i < ra->high_low_container.size; ++i) { + container_printf_as_uint32_array( + ra->high_low_container.containers[i], + ra->high_low_container.typecodes[i], + ((uint32_t)ra->high_low_container.keys[i]) << 16); + if (i + 1 < ra->high_low_container.size) printf(","); + } + printf("}"); +} + +void roaring_bitmap_printf_describe(const roaring_bitmap_t *ra) { + printf("{"); + for (int i = 0; i < ra->high_low_container.size; ++i) { + printf("%d: %s (%d)", ra->high_low_container.keys[i], + get_full_container_name(ra->high_low_container.containers[i], + ra->high_low_container.typecodes[i]), + container_get_cardinality(ra->high_low_container.containers[i], + ra->high_low_container.typecodes[i])); + if (ra->high_low_container.typecodes[i] == SHARED_CONTAINER_TYPE_CODE) { + printf( + "(shared count = %" PRIu32 " )", + ((shared_container_t *)(ra->high_low_container.containers[i])) + ->counter); + } + + if (i + 1 < ra->high_low_container.size) printf(", "); + } + printf("}"); +} + +typedef struct min_max_sum_s { + uint32_t min; + uint32_t max; + uint64_t sum; +} min_max_sum_t; + +static bool min_max_sum_fnc(uint32_t value, void *param) { + min_max_sum_t *mms = (min_max_sum_t *)param; + if (value > mms->max) mms->max = value; + if (value < mms->min) mms->min = value; + mms->sum += value; + return true; // we always process all data points +} + +/** +* (For advanced users.) +* Collect statistics about the bitmap +*/ +void roaring_bitmap_statistics(const roaring_bitmap_t *ra, + roaring_statistics_t *stat) { + memset(stat, 0, sizeof(*stat)); + stat->n_containers = ra->high_low_container.size; + stat->cardinality = roaring_bitmap_get_cardinality(ra); + min_max_sum_t mms; + mms.min = UINT32_C(0xFFFFFFFF); + mms.max = UINT32_C(0); + mms.sum = 0; + roaring_iterate(ra, &min_max_sum_fnc, &mms); + stat->min_value = mms.min; + stat->max_value = mms.max; + stat->sum_value = mms.sum; + + for (int i = 0; i < ra->high_low_container.size; ++i) { + uint8_t truetype = + get_container_type(ra->high_low_container.containers[i], + ra->high_low_container.typecodes[i]); + uint32_t card = + container_get_cardinality(ra->high_low_container.containers[i], + ra->high_low_container.typecodes[i]); + uint32_t sbytes = + container_size_in_bytes(ra->high_low_container.containers[i], + ra->high_low_container.typecodes[i]); + switch (truetype) { + case BITSET_CONTAINER_TYPE_CODE: + stat->n_bitset_containers++; + stat->n_values_bitset_containers += card; + stat->n_bytes_bitset_containers += sbytes; + break; + case ARRAY_CONTAINER_TYPE_CODE: + stat->n_array_containers++; + stat->n_values_array_containers += card; + stat->n_bytes_array_containers += sbytes; + break; + case RUN_CONTAINER_TYPE_CODE: + stat->n_run_containers++; + stat->n_values_run_containers += card; + stat->n_bytes_run_containers += sbytes; + break; + default: + assert(false); + __builtin_unreachable(); + } + } +} + +roaring_bitmap_t *roaring_bitmap_copy(const roaring_bitmap_t *r) { + roaring_bitmap_t *ans = + (roaring_bitmap_t *)malloc(sizeof(roaring_bitmap_t)); + if (!ans) { + return NULL; + } + bool is_ok = ra_copy(&r->high_low_container, &ans->high_low_container, + is_cow(r)); + if (!is_ok) { + free(ans); + return NULL; + } + roaring_bitmap_set_copy_on_write(ans, is_cow(r)); + return ans; +} + +bool roaring_bitmap_overwrite(roaring_bitmap_t *dest, + const roaring_bitmap_t *src) { + return ra_overwrite(&src->high_low_container, &dest->high_low_container, + is_cow(src)); +} + +void roaring_bitmap_free(const roaring_bitmap_t *r) { + if (!is_frozen(r)) { + ra_clear((roaring_array_t*)&r->high_low_container); + } + free((roaring_bitmap_t*)r); +} + +void roaring_bitmap_clear(roaring_bitmap_t *r) { + ra_reset(&r->high_low_container); +} + +void roaring_bitmap_add(roaring_bitmap_t *r, uint32_t val) { + const uint16_t hb = val >> 16; + const int i = ra_get_index(&r->high_low_container, hb); + uint8_t typecode; + if (i >= 0) { + ra_unshare_container_at_index(&r->high_low_container, i); + void *container = + ra_get_container_at_index(&r->high_low_container, i, &typecode); + uint8_t newtypecode = typecode; + void *container2 = + container_add(container, val & 0xFFFF, typecode, &newtypecode); + if (container2 != container) { + container_free(container, typecode); + ra_set_container_at_index(&r->high_low_container, i, container2, + newtypecode); + } + } else { + array_container_t *newac = array_container_create(); + void *container = container_add(newac, val & 0xFFFF, + ARRAY_CONTAINER_TYPE_CODE, &typecode); + // we could just assume that it stays an array container + ra_insert_new_key_value_at(&r->high_low_container, -i - 1, hb, + container, typecode); + } +} + +bool roaring_bitmap_add_checked(roaring_bitmap_t *r, uint32_t val) { + const uint16_t hb = val >> 16; + const int i = ra_get_index(&r->high_low_container, hb); + uint8_t typecode; + bool result = false; + if (i >= 0) { + ra_unshare_container_at_index(&r->high_low_container, i); + void *container = + ra_get_container_at_index(&r->high_low_container, i, &typecode); + + const int oldCardinality = + container_get_cardinality(container, typecode); + + uint8_t newtypecode = typecode; + void *container2 = + container_add(container, val & 0xFFFF, typecode, &newtypecode); + if (container2 != container) { + container_free(container, typecode); + ra_set_container_at_index(&r->high_low_container, i, container2, + newtypecode); + result = true; + } else { + const int newCardinality = + container_get_cardinality(container, newtypecode); + + result = oldCardinality != newCardinality; + } + } else { + array_container_t *newac = array_container_create(); + void *container = container_add(newac, val & 0xFFFF, + ARRAY_CONTAINER_TYPE_CODE, &typecode); + // we could just assume that it stays an array container + ra_insert_new_key_value_at(&r->high_low_container, -i - 1, hb, + container, typecode); + result = true; + } + + return result; +} + +void roaring_bitmap_remove(roaring_bitmap_t *r, uint32_t val) { + const uint16_t hb = val >> 16; + const int i = ra_get_index(&r->high_low_container, hb); + uint8_t typecode; + if (i >= 0) { + ra_unshare_container_at_index(&r->high_low_container, i); + void *container = + ra_get_container_at_index(&r->high_low_container, i, &typecode); + uint8_t newtypecode = typecode; + void *container2 = + container_remove(container, val & 0xFFFF, typecode, &newtypecode); + if (container2 != container) { + container_free(container, typecode); + ra_set_container_at_index(&r->high_low_container, i, container2, + newtypecode); + } + if (container_get_cardinality(container2, newtypecode) != 0) { + ra_set_container_at_index(&r->high_low_container, i, container2, + newtypecode); + } else { + ra_remove_at_index_and_free(&r->high_low_container, i); + } + } +} + +bool roaring_bitmap_remove_checked(roaring_bitmap_t *r, uint32_t val) { + const uint16_t hb = val >> 16; + const int i = ra_get_index(&r->high_low_container, hb); + uint8_t typecode; + bool result = false; + if (i >= 0) { + ra_unshare_container_at_index(&r->high_low_container, i); + void *container = + ra_get_container_at_index(&r->high_low_container, i, &typecode); + + const int oldCardinality = + container_get_cardinality(container, typecode); + + uint8_t newtypecode = typecode; + void *container2 = + container_remove(container, val & 0xFFFF, typecode, &newtypecode); + if (container2 != container) { + container_free(container, typecode); + ra_set_container_at_index(&r->high_low_container, i, container2, + newtypecode); + } + + const int newCardinality = + container_get_cardinality(container2, newtypecode); + + if (newCardinality != 0) { + ra_set_container_at_index(&r->high_low_container, i, container2, + newtypecode); + } else { + ra_remove_at_index_and_free(&r->high_low_container, i); + } + + result = oldCardinality != newCardinality; + } + return result; +} + +void roaring_bitmap_remove_many(roaring_bitmap_t *r, size_t n_args, + const uint32_t *vals) { + if (n_args == 0 || r->high_low_container.size == 0) { + return; + } + int32_t pos = -1; // position of the container used in the previous iteration + for (size_t i = 0; i < n_args; i++) { + uint16_t key = (uint16_t)(vals[i] >> 16); + if (pos < 0 || key != r->high_low_container.keys[pos]) { + pos = ra_get_index(&r->high_low_container, key); + } + if (pos >= 0) { + uint8_t new_typecode; + void *new_container; + new_container = container_remove(r->high_low_container.containers[pos], + vals[i] & 0xffff, + r->high_low_container.typecodes[pos], + &new_typecode); + if (new_container != r->high_low_container.containers[pos]) { + container_free(r->high_low_container.containers[pos], + r->high_low_container.typecodes[pos]); + ra_replace_key_and_container_at_index(&r->high_low_container, + pos, key, new_container, + new_typecode); + } + if (!container_nonzero_cardinality(new_container, new_typecode)) { + container_free(new_container, new_typecode); + ra_remove_at_index(&r->high_low_container, pos); + pos = -1; + } + } + } +} + +// there should be some SIMD optimizations possible here +roaring_bitmap_t *roaring_bitmap_and(const roaring_bitmap_t *x1, + const roaring_bitmap_t *x2) { + uint8_t container_result_type = 0; + const int length1 = x1->high_low_container.size, + length2 = x2->high_low_container.size; + uint32_t neededcap = length1 > length2 ? length2 : length1; + roaring_bitmap_t *answer = roaring_bitmap_create_with_capacity(neededcap); + roaring_bitmap_set_copy_on_write(answer, is_cow(x1) && is_cow(x2)); + + int pos1 = 0, pos2 = 0; + + while (pos1 < length1 && pos2 < length2) { + const uint16_t s1 = ra_get_key_at_index(&x1->high_low_container, pos1); + const uint16_t s2 = ra_get_key_at_index(&x2->high_low_container, pos2); + + if (s1 == s2) { + uint8_t container_type_1, container_type_2; + void *c1 = ra_get_container_at_index(&x1->high_low_container, pos1, + &container_type_1); + void *c2 = ra_get_container_at_index(&x2->high_low_container, pos2, + &container_type_2); + void *c = container_and(c1, container_type_1, c2, container_type_2, + &container_result_type); + if (container_nonzero_cardinality(c, container_result_type)) { + ra_append(&answer->high_low_container, s1, c, + container_result_type); + } else { + container_free( + c, container_result_type); // otherwise:memory leak! + } + ++pos1; + ++pos2; + } else if (s1 < s2) { // s1 < s2 + pos1 = ra_advance_until(&x1->high_low_container, s2, pos1); + } else { // s1 > s2 + pos2 = ra_advance_until(&x2->high_low_container, s1, pos2); + } + } + return answer; +} + +/** + * Compute the union of 'number' bitmaps. + */ +roaring_bitmap_t *roaring_bitmap_or_many(size_t number, + const roaring_bitmap_t **x) { + if (number == 0) { + return roaring_bitmap_create(); + } + if (number == 1) { + return roaring_bitmap_copy(x[0]); + } + roaring_bitmap_t *answer = + roaring_bitmap_lazy_or(x[0], x[1], LAZY_OR_BITSET_CONVERSION); + for (size_t i = 2; i < number; i++) { + roaring_bitmap_lazy_or_inplace(answer, x[i], LAZY_OR_BITSET_CONVERSION); + } + roaring_bitmap_repair_after_lazy(answer); + return answer; +} + +/** + * Compute the xor of 'number' bitmaps. + */ +roaring_bitmap_t *roaring_bitmap_xor_many(size_t number, + const roaring_bitmap_t **x) { + if (number == 0) { + return roaring_bitmap_create(); + } + if (number == 1) { + return roaring_bitmap_copy(x[0]); + } + roaring_bitmap_t *answer = roaring_bitmap_lazy_xor(x[0], x[1]); + for (size_t i = 2; i < number; i++) { + roaring_bitmap_lazy_xor_inplace(answer, x[i]); + } + roaring_bitmap_repair_after_lazy(answer); + return answer; +} + +// inplace and (modifies its first argument). +void roaring_bitmap_and_inplace(roaring_bitmap_t *x1, + const roaring_bitmap_t *x2) { + if (x1 == x2) return; + int pos1 = 0, pos2 = 0, intersection_size = 0; + const int length1 = ra_get_size(&x1->high_low_container); + const int length2 = ra_get_size(&x2->high_low_container); + + // any skipped-over or newly emptied containers in x1 + // have to be freed. + while (pos1 < length1 && pos2 < length2) { + const uint16_t s1 = ra_get_key_at_index(&x1->high_low_container, pos1); + const uint16_t s2 = ra_get_key_at_index(&x2->high_low_container, pos2); + + if (s1 == s2) { + uint8_t typecode1, typecode2, typecode_result; + void *c1 = ra_get_container_at_index(&x1->high_low_container, pos1, + &typecode1); + c1 = get_writable_copy_if_shared(c1, &typecode1); + void *c2 = ra_get_container_at_index(&x2->high_low_container, pos2, + &typecode2); + void *c = + container_iand(c1, typecode1, c2, typecode2, &typecode_result); + if (c != c1) { // in this instance a new container was created, and + // we need to free the old one + container_free(c1, typecode1); + } + if (container_nonzero_cardinality(c, typecode_result)) { + ra_replace_key_and_container_at_index(&x1->high_low_container, + intersection_size, s1, c, + typecode_result); + intersection_size++; + } else { + container_free(c, typecode_result); + } + ++pos1; + ++pos2; + } else if (s1 < s2) { + pos1 = ra_advance_until_freeing(&x1->high_low_container, s2, pos1); + } else { // s1 > s2 + pos2 = ra_advance_until(&x2->high_low_container, s1, pos2); + } + } + + // if we ended early because x2 ran out, then all remaining in x1 should be + // freed + while (pos1 < length1) { + container_free(x1->high_low_container.containers[pos1], + x1->high_low_container.typecodes[pos1]); + ++pos1; + } + + // all containers after this have either been copied or freed + ra_downsize(&x1->high_low_container, intersection_size); +} + +roaring_bitmap_t *roaring_bitmap_or(const roaring_bitmap_t *x1, + const roaring_bitmap_t *x2) { + uint8_t container_result_type = 0; + const int length1 = x1->high_low_container.size, + length2 = x2->high_low_container.size; + if (0 == length1) { + return roaring_bitmap_copy(x2); + } + if (0 == length2) { + return roaring_bitmap_copy(x1); + } + roaring_bitmap_t *answer = + roaring_bitmap_create_with_capacity(length1 + length2); + roaring_bitmap_set_copy_on_write(answer, is_cow(x1) && is_cow(x2)); + int pos1 = 0, pos2 = 0; + uint8_t container_type_1, container_type_2; + uint16_t s1 = ra_get_key_at_index(&x1->high_low_container, pos1); + uint16_t s2 = ra_get_key_at_index(&x2->high_low_container, pos2); + while (true) { + if (s1 == s2) { + void *c1 = ra_get_container_at_index(&x1->high_low_container, pos1, + &container_type_1); + void *c2 = ra_get_container_at_index(&x2->high_low_container, pos2, + &container_type_2); + void *c = container_or(c1, container_type_1, c2, container_type_2, + &container_result_type); + // since we assume that the initial containers are non-empty, the + // result here + // can only be non-empty + ra_append(&answer->high_low_container, s1, c, + container_result_type); + ++pos1; + ++pos2; + if (pos1 == length1) break; + if (pos2 == length2) break; + s1 = ra_get_key_at_index(&x1->high_low_container, pos1); + s2 = ra_get_key_at_index(&x2->high_low_container, pos2); + + } else if (s1 < s2) { // s1 < s2 + void *c1 = ra_get_container_at_index(&x1->high_low_container, pos1, + &container_type_1); + // c1 = container_clone(c1, container_type_1); + c1 = + get_copy_of_container(c1, &container_type_1, is_cow(x1)); + if (is_cow(x1)) { + ra_set_container_at_index(&x1->high_low_container, pos1, c1, + container_type_1); + } + ra_append(&answer->high_low_container, s1, c1, container_type_1); + pos1++; + if (pos1 == length1) break; + s1 = ra_get_key_at_index(&x1->high_low_container, pos1); + + } else { // s1 > s2 + void *c2 = ra_get_container_at_index(&x2->high_low_container, pos2, + &container_type_2); + // c2 = container_clone(c2, container_type_2); + c2 = + get_copy_of_container(c2, &container_type_2, is_cow(x2)); + if (is_cow(x2)) { + ra_set_container_at_index(&x2->high_low_container, pos2, c2, + container_type_2); + } + ra_append(&answer->high_low_container, s2, c2, container_type_2); + pos2++; + if (pos2 == length2) break; + s2 = ra_get_key_at_index(&x2->high_low_container, pos2); + } + } + if (pos1 == length1) { + ra_append_copy_range(&answer->high_low_container, + &x2->high_low_container, pos2, length2, + is_cow(x2)); + } else if (pos2 == length2) { + ra_append_copy_range(&answer->high_low_container, + &x1->high_low_container, pos1, length1, + is_cow(x1)); + } + return answer; +} + +// inplace or (modifies its first argument). +void roaring_bitmap_or_inplace(roaring_bitmap_t *x1, + const roaring_bitmap_t *x2) { + uint8_t container_result_type = 0; + int length1 = x1->high_low_container.size; + const int length2 = x2->high_low_container.size; + + if (0 == length2) return; + + if (0 == length1) { + roaring_bitmap_overwrite(x1, x2); + return; + } + int pos1 = 0, pos2 = 0; + uint8_t container_type_1, container_type_2; + uint16_t s1 = ra_get_key_at_index(&x1->high_low_container, pos1); + uint16_t s2 = ra_get_key_at_index(&x2->high_low_container, pos2); + while (true) { + if (s1 == s2) { + void *c1 = ra_get_container_at_index(&x1->high_low_container, pos1, + &container_type_1); + if (!container_is_full(c1, container_type_1)) { + c1 = get_writable_copy_if_shared(c1, &container_type_1); + + void *c2 = ra_get_container_at_index(&x2->high_low_container, + pos2, &container_type_2); + void *c = + container_ior(c1, container_type_1, c2, container_type_2, + &container_result_type); + if (c != + c1) { // in this instance a new container was created, and + // we need to free the old one + container_free(c1, container_type_1); + } + + ra_set_container_at_index(&x1->high_low_container, pos1, c, + container_result_type); + } + ++pos1; + ++pos2; + if (pos1 == length1) break; + if (pos2 == length2) break; + s1 = ra_get_key_at_index(&x1->high_low_container, pos1); + s2 = ra_get_key_at_index(&x2->high_low_container, pos2); + + } else if (s1 < s2) { // s1 < s2 + pos1++; + if (pos1 == length1) break; + s1 = ra_get_key_at_index(&x1->high_low_container, pos1); + + } else { // s1 > s2 + void *c2 = ra_get_container_at_index(&x2->high_low_container, pos2, + &container_type_2); + c2 = + get_copy_of_container(c2, &container_type_2, is_cow(x2)); + if (is_cow(x2)) { + ra_set_container_at_index(&x2->high_low_container, pos2, c2, + container_type_2); + } + + // void *c2_clone = container_clone(c2, container_type_2); + ra_insert_new_key_value_at(&x1->high_low_container, pos1, s2, c2, + container_type_2); + pos1++; + length1++; + pos2++; + if (pos2 == length2) break; + s2 = ra_get_key_at_index(&x2->high_low_container, pos2); + } + } + if (pos1 == length1) { + ra_append_copy_range(&x1->high_low_container, &x2->high_low_container, + pos2, length2, is_cow(x2)); + } +} + +roaring_bitmap_t *roaring_bitmap_xor(const roaring_bitmap_t *x1, + const roaring_bitmap_t *x2) { + uint8_t container_result_type = 0; + const int length1 = x1->high_low_container.size, + length2 = x2->high_low_container.size; + if (0 == length1) { + return roaring_bitmap_copy(x2); + } + if (0 == length2) { + return roaring_bitmap_copy(x1); + } + roaring_bitmap_t *answer = + roaring_bitmap_create_with_capacity(length1 + length2); + roaring_bitmap_set_copy_on_write(answer, is_cow(x1) && is_cow(x2)); + int pos1 = 0, pos2 = 0; + uint8_t container_type_1, container_type_2; + uint16_t s1 = ra_get_key_at_index(&x1->high_low_container, pos1); + uint16_t s2 = ra_get_key_at_index(&x2->high_low_container, pos2); + while (true) { + if (s1 == s2) { + void *c1 = ra_get_container_at_index(&x1->high_low_container, pos1, + &container_type_1); + void *c2 = ra_get_container_at_index(&x2->high_low_container, pos2, + &container_type_2); + void *c = container_xor(c1, container_type_1, c2, container_type_2, + &container_result_type); + + if (container_nonzero_cardinality(c, container_result_type)) { + ra_append(&answer->high_low_container, s1, c, + container_result_type); + } else { + container_free(c, container_result_type); + } + ++pos1; + ++pos2; + if (pos1 == length1) break; + if (pos2 == length2) break; + s1 = ra_get_key_at_index(&x1->high_low_container, pos1); + s2 = ra_get_key_at_index(&x2->high_low_container, pos2); + + } else if (s1 < s2) { // s1 < s2 + void *c1 = ra_get_container_at_index(&x1->high_low_container, pos1, + &container_type_1); + c1 = + get_copy_of_container(c1, &container_type_1, is_cow(x1)); + if (is_cow(x1)) { + ra_set_container_at_index(&x1->high_low_container, pos1, c1, + container_type_1); + } + ra_append(&answer->high_low_container, s1, c1, container_type_1); + pos1++; + if (pos1 == length1) break; + s1 = ra_get_key_at_index(&x1->high_low_container, pos1); + + } else { // s1 > s2 + void *c2 = ra_get_container_at_index(&x2->high_low_container, pos2, + &container_type_2); + c2 = + get_copy_of_container(c2, &container_type_2, is_cow(x2)); + if (is_cow(x2)) { + ra_set_container_at_index(&x2->high_low_container, pos2, c2, + container_type_2); + } + ra_append(&answer->high_low_container, s2, c2, container_type_2); + pos2++; + if (pos2 == length2) break; + s2 = ra_get_key_at_index(&x2->high_low_container, pos2); + } + } + if (pos1 == length1) { + ra_append_copy_range(&answer->high_low_container, + &x2->high_low_container, pos2, length2, + is_cow(x2)); + } else if (pos2 == length2) { + ra_append_copy_range(&answer->high_low_container, + &x1->high_low_container, pos1, length1, + is_cow(x1)); + } + return answer; +} + +// inplace xor (modifies its first argument). + +void roaring_bitmap_xor_inplace(roaring_bitmap_t *x1, + const roaring_bitmap_t *x2) { + assert(x1 != x2); + uint8_t container_result_type = 0; + int length1 = x1->high_low_container.size; + const int length2 = x2->high_low_container.size; + + if (0 == length2) return; + + if (0 == length1) { + roaring_bitmap_overwrite(x1, x2); + return; + } + + // XOR can have new containers inserted from x2, but can also + // lose containers when x1 and x2 are nonempty and identical. + + int pos1 = 0, pos2 = 0; + uint8_t container_type_1, container_type_2; + uint16_t s1 = ra_get_key_at_index(&x1->high_low_container, pos1); + uint16_t s2 = ra_get_key_at_index(&x2->high_low_container, pos2); + while (true) { + if (s1 == s2) { + void *c1 = ra_get_container_at_index(&x1->high_low_container, pos1, + &container_type_1); + c1 = get_writable_copy_if_shared(c1, &container_type_1); + + void *c2 = ra_get_container_at_index(&x2->high_low_container, pos2, + &container_type_2); + void *c = container_ixor(c1, container_type_1, c2, container_type_2, + &container_result_type); + + if (container_nonzero_cardinality(c, container_result_type)) { + ra_set_container_at_index(&x1->high_low_container, pos1, c, + container_result_type); + ++pos1; + } else { + container_free(c, container_result_type); + ra_remove_at_index(&x1->high_low_container, pos1); + --length1; + } + + ++pos2; + if (pos1 == length1) break; + if (pos2 == length2) break; + s1 = ra_get_key_at_index(&x1->high_low_container, pos1); + s2 = ra_get_key_at_index(&x2->high_low_container, pos2); + + } else if (s1 < s2) { // s1 < s2 + pos1++; + if (pos1 == length1) break; + s1 = ra_get_key_at_index(&x1->high_low_container, pos1); + + } else { // s1 > s2 + void *c2 = ra_get_container_at_index(&x2->high_low_container, pos2, + &container_type_2); + c2 = + get_copy_of_container(c2, &container_type_2, is_cow(x2)); + if (is_cow(x2)) { + ra_set_container_at_index(&x2->high_low_container, pos2, c2, + container_type_2); + } + + ra_insert_new_key_value_at(&x1->high_low_container, pos1, s2, c2, + container_type_2); + pos1++; + length1++; + pos2++; + if (pos2 == length2) break; + s2 = ra_get_key_at_index(&x2->high_low_container, pos2); + } + } + if (pos1 == length1) { + ra_append_copy_range(&x1->high_low_container, &x2->high_low_container, + pos2, length2, is_cow(x2)); + } +} + +roaring_bitmap_t *roaring_bitmap_andnot(const roaring_bitmap_t *x1, + const roaring_bitmap_t *x2) { + uint8_t container_result_type = 0; + const int length1 = x1->high_low_container.size, + length2 = x2->high_low_container.size; + if (0 == length1) { + roaring_bitmap_t *empty_bitmap = roaring_bitmap_create(); + roaring_bitmap_set_copy_on_write(empty_bitmap, is_cow(x1) && is_cow(x2)); + return empty_bitmap; + } + if (0 == length2) { + return roaring_bitmap_copy(x1); + } + roaring_bitmap_t *answer = roaring_bitmap_create_with_capacity(length1); + roaring_bitmap_set_copy_on_write(answer, is_cow(x1) && is_cow(x2)); + + int pos1 = 0, pos2 = 0; + uint8_t container_type_1, container_type_2; + uint16_t s1 = 0; + uint16_t s2 = 0; + while (true) { + s1 = ra_get_key_at_index(&x1->high_low_container, pos1); + s2 = ra_get_key_at_index(&x2->high_low_container, pos2); + + if (s1 == s2) { + void *c1 = ra_get_container_at_index(&x1->high_low_container, pos1, + &container_type_1); + void *c2 = ra_get_container_at_index(&x2->high_low_container, pos2, + &container_type_2); + void *c = + container_andnot(c1, container_type_1, c2, container_type_2, + &container_result_type); + + if (container_nonzero_cardinality(c, container_result_type)) { + ra_append(&answer->high_low_container, s1, c, + container_result_type); + } else { + container_free(c, container_result_type); + } + ++pos1; + ++pos2; + if (pos1 == length1) break; + if (pos2 == length2) break; + } else if (s1 < s2) { // s1 < s2 + const int next_pos1 = + ra_advance_until(&x1->high_low_container, s2, pos1); + ra_append_copy_range(&answer->high_low_container, + &x1->high_low_container, pos1, next_pos1, + is_cow(x1)); + // TODO : perhaps some of the copy_on_write should be based on + // answer rather than x1 (more stringent?). Many similar cases + pos1 = next_pos1; + if (pos1 == length1) break; + } else { // s1 > s2 + pos2 = ra_advance_until(&x2->high_low_container, s1, pos2); + if (pos2 == length2) break; + } + } + if (pos2 == length2) { + ra_append_copy_range(&answer->high_low_container, + &x1->high_low_container, pos1, length1, + is_cow(x1)); + } + return answer; +} + +// inplace andnot (modifies its first argument). + +void roaring_bitmap_andnot_inplace(roaring_bitmap_t *x1, + const roaring_bitmap_t *x2) { + assert(x1 != x2); + + uint8_t container_result_type = 0; + int length1 = x1->high_low_container.size; + const int length2 = x2->high_low_container.size; + int intersection_size = 0; + + if (0 == length2) return; + + if (0 == length1) { + roaring_bitmap_clear(x1); + return; + } + + int pos1 = 0, pos2 = 0; + uint8_t container_type_1, container_type_2; + uint16_t s1 = ra_get_key_at_index(&x1->high_low_container, pos1); + uint16_t s2 = ra_get_key_at_index(&x2->high_low_container, pos2); + while (true) { + if (s1 == s2) { + void *c1 = ra_get_container_at_index(&x1->high_low_container, pos1, + &container_type_1); + c1 = get_writable_copy_if_shared(c1, &container_type_1); + + void *c2 = ra_get_container_at_index(&x2->high_low_container, pos2, + &container_type_2); + void *c = + container_iandnot(c1, container_type_1, c2, container_type_2, + &container_result_type); + + if (container_nonzero_cardinality(c, container_result_type)) { + ra_replace_key_and_container_at_index(&x1->high_low_container, + intersection_size++, s1, + c, container_result_type); + } else { + container_free(c, container_result_type); + } + + ++pos1; + ++pos2; + if (pos1 == length1) break; + if (pos2 == length2) break; + s1 = ra_get_key_at_index(&x1->high_low_container, pos1); + s2 = ra_get_key_at_index(&x2->high_low_container, pos2); + + } else if (s1 < s2) { // s1 < s2 + if (pos1 != intersection_size) { + void *c1 = ra_get_container_at_index(&x1->high_low_container, + pos1, &container_type_1); + + ra_replace_key_and_container_at_index(&x1->high_low_container, + intersection_size, s1, c1, + container_type_1); + } + intersection_size++; + pos1++; + if (pos1 == length1) break; + s1 = ra_get_key_at_index(&x1->high_low_container, pos1); + + } else { // s1 > s2 + pos2 = ra_advance_until(&x2->high_low_container, s1, pos2); + if (pos2 == length2) break; + s2 = ra_get_key_at_index(&x2->high_low_container, pos2); + } + } + + if (pos1 < length1) { + // all containers between intersection_size and + // pos1 are junk. However, they have either been moved + // (thus still referenced) or involved in an iandnot + // that will clean up all containers that could not be reused. + // Thus we should not free the junk containers between + // intersection_size and pos1. + if (pos1 > intersection_size) { + // left slide of remaining items + ra_copy_range(&x1->high_low_container, pos1, length1, + intersection_size); + } + // else current placement is fine + intersection_size += (length1 - pos1); + } + ra_downsize(&x1->high_low_container, intersection_size); +} + +uint64_t roaring_bitmap_get_cardinality(const roaring_bitmap_t *ra) { + uint64_t card = 0; + for (int i = 0; i < ra->high_low_container.size; ++i) + card += container_get_cardinality(ra->high_low_container.containers[i], + ra->high_low_container.typecodes[i]); + return card; +} + +uint64_t roaring_bitmap_range_cardinality(const roaring_bitmap_t *ra, + uint64_t range_start, + uint64_t range_end) { + if (range_end > UINT32_MAX) { + range_end = UINT32_MAX + UINT64_C(1); + } + if (range_start >= range_end) { + return 0; + } + range_end--; // make range_end inclusive + // now we have: 0 <= range_start <= range_end <= UINT32_MAX + + uint16_t minhb = range_start >> 16; + uint16_t maxhb = range_end >> 16; + + uint64_t card = 0; + + int i = ra_get_index(&ra->high_low_container, minhb); + if (i >= 0) { + if (minhb == maxhb) { + card += container_rank(ra->high_low_container.containers[i], + ra->high_low_container.typecodes[i], + range_end & 0xffff); + } else { + card += container_get_cardinality(ra->high_low_container.containers[i], + ra->high_low_container.typecodes[i]); + } + if ((range_start & 0xffff) != 0) { + card -= container_rank(ra->high_low_container.containers[i], + ra->high_low_container.typecodes[i], + (range_start & 0xffff) - 1); + } + i++; + } else { + i = -i - 1; + } + + for (; i < ra->high_low_container.size; i++) { + uint16_t key = ra->high_low_container.keys[i]; + if (key < maxhb) { + card += container_get_cardinality(ra->high_low_container.containers[i], + ra->high_low_container.typecodes[i]); + } else if (key == maxhb) { + card += container_rank(ra->high_low_container.containers[i], + ra->high_low_container.typecodes[i], + range_end & 0xffff); + break; + } else { + break; + } + } + + return card; +} + + +bool roaring_bitmap_is_empty(const roaring_bitmap_t *ra) { + return ra->high_low_container.size == 0; +} + +void roaring_bitmap_to_uint32_array(const roaring_bitmap_t *ra, uint32_t *ans) { + ra_to_uint32_array(&ra->high_low_container, ans); +} + +bool roaring_bitmap_range_uint32_array(const roaring_bitmap_t *ra, size_t offset, size_t limit, uint32_t *ans) { + return ra_range_uint32_array(&ra->high_low_container, offset, limit, ans); +} + +/** convert array and bitmap containers to run containers when it is more + * efficient; + * also convert from run containers when more space efficient. Returns + * true if the result has at least one run container. +*/ +bool roaring_bitmap_run_optimize(roaring_bitmap_t *r) { + bool answer = false; + for (int i = 0; i < r->high_low_container.size; i++) { + uint8_t typecode_original, typecode_after; + ra_unshare_container_at_index( + &r->high_low_container, i); // TODO: this introduces extra cloning! + void *c = ra_get_container_at_index(&r->high_low_container, i, + &typecode_original); + void *c1 = convert_run_optimize(c, typecode_original, &typecode_after); + if (typecode_after == RUN_CONTAINER_TYPE_CODE) answer = true; + ra_set_container_at_index(&r->high_low_container, i, c1, + typecode_after); + } + return answer; +} + +size_t roaring_bitmap_shrink_to_fit(roaring_bitmap_t *r) { + size_t answer = 0; + for (int i = 0; i < r->high_low_container.size; i++) { + uint8_t typecode_original; + void *c = ra_get_container_at_index(&r->high_low_container, i, + &typecode_original); + answer += container_shrink_to_fit(c, typecode_original); + } + answer += ra_shrink_to_fit(&r->high_low_container); + return answer; +} + +/** + * Remove run-length encoding even when it is more space efficient + * return whether a change was applied + */ +bool roaring_bitmap_remove_run_compression(roaring_bitmap_t *r) { + bool answer = false; + for (int i = 0; i < r->high_low_container.size; i++) { + uint8_t typecode_original, typecode_after; + void *c = ra_get_container_at_index(&r->high_low_container, i, + &typecode_original); + if (get_container_type(c, typecode_original) == + RUN_CONTAINER_TYPE_CODE) { + answer = true; + if (typecode_original == SHARED_CONTAINER_TYPE_CODE) { + run_container_t *truec = + (run_container_t *)((shared_container_t *)c)->container; + int32_t card = run_container_cardinality(truec); + void *c1 = convert_to_bitset_or_array_container( + truec, card, &typecode_after); + shared_container_free((shared_container_t *)c);// will free the run container as needed + ra_set_container_at_index(&r->high_low_container, i, c1, + typecode_after); + + } else { + int32_t card = run_container_cardinality((run_container_t *)c); + void *c1 = convert_to_bitset_or_array_container( + (run_container_t *)c, card, &typecode_after); + run_container_free((run_container_t *)c); + ra_set_container_at_index(&r->high_low_container, i, c1, + typecode_after); + } + } + } + return answer; +} + +size_t roaring_bitmap_serialize(const roaring_bitmap_t *ra, char *buf) { + size_t portablesize = roaring_bitmap_portable_size_in_bytes(ra); + uint64_t cardinality = roaring_bitmap_get_cardinality(ra); + uint64_t sizeasarray = cardinality * sizeof(uint32_t) + sizeof(uint32_t); + if (portablesize < sizeasarray) { + buf[0] = SERIALIZATION_CONTAINER; + return roaring_bitmap_portable_serialize(ra, buf + 1) + 1; + } else { + buf[0] = SERIALIZATION_ARRAY_UINT32; + memcpy(buf + 1, &cardinality, sizeof(uint32_t)); + roaring_bitmap_to_uint32_array( + ra, (uint32_t *)(buf + 1 + sizeof(uint32_t))); + return 1 + (size_t)sizeasarray; + } +} + +size_t roaring_bitmap_size_in_bytes(const roaring_bitmap_t *ra) { + size_t portablesize = roaring_bitmap_portable_size_in_bytes(ra); + uint64_t sizeasarray = roaring_bitmap_get_cardinality(ra) * sizeof(uint32_t) + + sizeof(uint32_t); + return portablesize < sizeasarray ? portablesize + 1 : (size_t)sizeasarray + 1; +} + +size_t roaring_bitmap_portable_size_in_bytes(const roaring_bitmap_t *ra) { + return ra_portable_size_in_bytes(&ra->high_low_container); +} + + +roaring_bitmap_t *roaring_bitmap_portable_deserialize_safe(const char *buf, size_t maxbytes) { + roaring_bitmap_t *ans = + (roaring_bitmap_t *)malloc(sizeof(roaring_bitmap_t)); + if (ans == NULL) { + return NULL; + } + size_t bytesread; + bool is_ok = ra_portable_deserialize(&ans->high_low_container, buf, maxbytes, &bytesread); + if(is_ok) assert(bytesread <= maxbytes); + roaring_bitmap_set_copy_on_write(ans, false); + if (!is_ok) { + free(ans); + return NULL; + } + return ans; +} + +roaring_bitmap_t *roaring_bitmap_portable_deserialize(const char *buf) { + return roaring_bitmap_portable_deserialize_safe(buf, SIZE_MAX); +} + + +size_t roaring_bitmap_portable_deserialize_size(const char *buf, size_t maxbytes) { + return ra_portable_deserialize_size(buf, maxbytes); +} + + +size_t roaring_bitmap_portable_serialize(const roaring_bitmap_t *ra, + char *buf) { + return ra_portable_serialize(&ra->high_low_container, buf); +} + +roaring_bitmap_t *roaring_bitmap_deserialize(const void *buf) { + const char *bufaschar = (const char *)buf; + if (*(const unsigned char *)buf == SERIALIZATION_ARRAY_UINT32) { + /* This looks like a compressed set of uint32_t elements */ + uint32_t card; + memcpy(&card, bufaschar + 1, sizeof(uint32_t)); + const uint32_t *elems = + (const uint32_t *)(bufaschar + 1 + sizeof(uint32_t)); + + return roaring_bitmap_of_ptr(card, elems); + } else if (bufaschar[0] == SERIALIZATION_CONTAINER) { + return roaring_bitmap_portable_deserialize(bufaschar + 1); + } else + return (NULL); +} + +bool roaring_iterate(const roaring_bitmap_t *ra, roaring_iterator iterator, + void *ptr) { + for (int i = 0; i < ra->high_low_container.size; ++i) + if (!container_iterate(ra->high_low_container.containers[i], + ra->high_low_container.typecodes[i], + ((uint32_t)ra->high_low_container.keys[i]) << 16, + iterator, ptr)) { + return false; + } + return true; +} + +bool roaring_iterate64(const roaring_bitmap_t *ra, roaring_iterator64 iterator, + uint64_t high_bits, void *ptr) { + for (int i = 0; i < ra->high_low_container.size; ++i) + if (!container_iterate64( + ra->high_low_container.containers[i], + ra->high_low_container.typecodes[i], + ((uint32_t)ra->high_low_container.keys[i]) << 16, iterator, + high_bits, ptr)) { + return false; + } + return true; +} + +/**** +* begin roaring_uint32_iterator_t +*****/ + +// Partially initializes the roaring iterator when it begins looking at +// a new container. +static bool iter_new_container_partial_init(roaring_uint32_iterator_t *newit) { + newit->in_container_index = 0; + newit->run_index = 0; + newit->current_value = 0; + if (newit->container_index >= newit->parent->high_low_container.size || + newit->container_index < 0) { + newit->current_value = UINT32_MAX; + return (newit->has_value = false); + } + // assume not empty + newit->has_value = true; + // we precompute container, typecode and highbits so that successive + // iterators do not have to grab them from odd memory locations + // and have to worry about the (easily predicted) container_unwrap_shared + // call. + newit->container = + newit->parent->high_low_container.containers[newit->container_index]; + newit->typecode = + newit->parent->high_low_container.typecodes[newit->container_index]; + newit->highbits = + ((uint32_t) + newit->parent->high_low_container.keys[newit->container_index]) + << 16; + newit->container = + container_unwrap_shared(newit->container, &(newit->typecode)); + return newit->has_value; +} + +static bool loadfirstvalue(roaring_uint32_iterator_t *newit) { + if (!iter_new_container_partial_init(newit)) + return newit->has_value; + + uint32_t wordindex; + uint64_t word; // used for bitsets + switch (newit->typecode) { + case BITSET_CONTAINER_TYPE_CODE: + wordindex = 0; + while ((word = ((const bitset_container_t *)(newit->container)) + ->array[wordindex]) == 0) + wordindex++; // advance + // here "word" is non-zero + newit->in_container_index = wordindex * 64 + __builtin_ctzll(word); + newit->current_value = newit->highbits | newit->in_container_index; + break; + case ARRAY_CONTAINER_TYPE_CODE: + newit->current_value = + newit->highbits | + ((const array_container_t *)(newit->container))->array[0]; + break; + case RUN_CONTAINER_TYPE_CODE: + newit->current_value = + newit->highbits | + (((const run_container_t *)(newit->container))->runs[0].value); + break; + default: + // if this ever happens, bug! + assert(false); + } // switch (typecode) + return true; +} + +static bool loadlastvalue(roaring_uint32_iterator_t* newit) { + if (!iter_new_container_partial_init(newit)) + return newit->has_value; + + switch(newit->typecode) { + case BITSET_CONTAINER_TYPE_CODE: { + uint32_t wordindex = BITSET_CONTAINER_SIZE_IN_WORDS - 1; + uint64_t word; + const bitset_container_t* bitset_container = (const bitset_container_t*)newit->container; + while ((word = bitset_container->array[wordindex]) == 0) + --wordindex; + + int num_leading_zeros = __builtin_clzll(word); + newit->in_container_index = (wordindex * 64) + (63 - num_leading_zeros); + newit->current_value = newit->highbits | newit->in_container_index; + break; + } + case ARRAY_CONTAINER_TYPE_CODE: { + const array_container_t* array_container = (const array_container_t*)newit->container; + newit->in_container_index = array_container->cardinality - 1; + newit->current_value = newit->highbits | array_container->array[newit->in_container_index]; + break; + } + case RUN_CONTAINER_TYPE_CODE: { + const run_container_t* run_container = (const run_container_t*)newit->container; + newit->run_index = run_container->n_runs - 1; + const rle16_t* last_run = &run_container->runs[newit->run_index]; + newit->current_value = newit->highbits | (last_run->value + last_run->length); + break; + } + default: + // if this ever happens, bug! + assert(false); + } + return true; +} + +// prerequesite: the value should be in range of the container +static bool loadfirstvalue_largeorequal(roaring_uint32_iterator_t *newit, uint32_t val) { + // Don't have to check return value because of prerequisite + iter_new_container_partial_init(newit); + uint16_t lb = val & 0xFFFF; + + switch (newit->typecode) { + case BITSET_CONTAINER_TYPE_CODE: + newit->in_container_index = bitset_container_index_equalorlarger((const bitset_container_t *)(newit->container), lb); + newit->current_value = newit->highbits | newit->in_container_index; + break; + case ARRAY_CONTAINER_TYPE_CODE: + newit->in_container_index = array_container_index_equalorlarger((const array_container_t *)(newit->container), lb); + newit->current_value = + newit->highbits | + ((const array_container_t *)(newit->container))->array[newit->in_container_index]; + break; + case RUN_CONTAINER_TYPE_CODE: + newit->run_index = run_container_index_equalorlarger((const run_container_t *)(newit->container), lb); + if(((const run_container_t *)(newit->container))->runs[newit->run_index].value <= lb) { + newit->current_value = val; + } else { + newit->current_value = + newit->highbits | + (((const run_container_t *)(newit->container))->runs[newit->run_index].value); + } + break; + default: + // if this ever happens, bug! + assert(false); + } // switch (typecode) + return true; +} + +void roaring_init_iterator(const roaring_bitmap_t *ra, + roaring_uint32_iterator_t *newit) { + newit->parent = ra; + newit->container_index = 0; + newit->has_value = loadfirstvalue(newit); +} + +void roaring_init_iterator_last(const roaring_bitmap_t *ra, + roaring_uint32_iterator_t *newit) { + newit->parent = ra; + newit->container_index = newit->parent->high_low_container.size - 1; + newit->has_value = loadlastvalue(newit); +} + +roaring_uint32_iterator_t *roaring_create_iterator(const roaring_bitmap_t *ra) { + roaring_uint32_iterator_t *newit = + (roaring_uint32_iterator_t *)malloc(sizeof(roaring_uint32_iterator_t)); + if (newit == NULL) return NULL; + roaring_init_iterator(ra, newit); + return newit; +} + +roaring_uint32_iterator_t *roaring_copy_uint32_iterator( + const roaring_uint32_iterator_t *it) { + roaring_uint32_iterator_t *newit = + (roaring_uint32_iterator_t *)malloc(sizeof(roaring_uint32_iterator_t)); + memcpy(newit, it, sizeof(roaring_uint32_iterator_t)); + return newit; +} + +bool roaring_move_uint32_iterator_equalorlarger(roaring_uint32_iterator_t *it, uint32_t val) { + uint16_t hb = val >> 16; + const int i = ra_get_index(& it->parent->high_low_container, hb); + if (i >= 0) { + uint32_t lowvalue = container_maximum(it->parent->high_low_container.containers[i], it->parent->high_low_container.typecodes[i]); + uint16_t lb = val & 0xFFFF; + if(lowvalue < lb ) { + it->container_index = i+1; // will have to load first value of next container + } else {// the value is necessarily within the range of the container + it->container_index = i; + it->has_value = loadfirstvalue_largeorequal(it, val); + return it->has_value; + } + } else { + // there is no matching, so we are going for the next container + it->container_index = -i-1; + } + it->has_value = loadfirstvalue(it); + return it->has_value; +} + + +bool roaring_advance_uint32_iterator(roaring_uint32_iterator_t *it) { + if (it->container_index >= it->parent->high_low_container.size) { + return (it->has_value = false); + } + if (it->container_index < 0) { + it->container_index = 0; + return (it->has_value = loadfirstvalue(it)); + } + + uint32_t wordindex; // used for bitsets + uint64_t word; // used for bitsets + switch (it->typecode) { + case BITSET_CONTAINER_TYPE_CODE: + it->in_container_index++; + wordindex = it->in_container_index / 64; + if (wordindex >= BITSET_CONTAINER_SIZE_IN_WORDS) break; + word = ((const bitset_container_t *)(it->container)) + ->array[wordindex] & + (UINT64_MAX << (it->in_container_index % 64)); + // next part could be optimized/simplified + while ((word == 0) && + (wordindex + 1 < BITSET_CONTAINER_SIZE_IN_WORDS)) { + wordindex++; + word = ((const bitset_container_t *)(it->container)) + ->array[wordindex]; + } + if (word != 0) { + it->in_container_index = wordindex * 64 + __builtin_ctzll(word); + it->current_value = it->highbits | it->in_container_index; + return (it->has_value = true); + } + break; + case ARRAY_CONTAINER_TYPE_CODE: + it->in_container_index++; + if (it->in_container_index < + ((const array_container_t *)(it->container))->cardinality) { + it->current_value = it->highbits | + ((const array_container_t *)(it->container)) + ->array[it->in_container_index]; + return (it->has_value = true); + } + break; + case RUN_CONTAINER_TYPE_CODE: { + if(it->current_value == UINT32_MAX) { + return (it->has_value = false); // without this, we risk an overflow to zero + } + + const run_container_t* run_container = (const run_container_t*)it->container; + if (++it->current_value <= (it->highbits | (run_container->runs[it->run_index].value + + run_container->runs[it->run_index].length))) { + return (it->has_value = true); + } + + if (++it->run_index < run_container->n_runs) { + // Assume the run has a value + it->current_value = it->highbits | run_container->runs[it->run_index].value; + return (it->has_value = true); + } + break; + } + default: + // if this ever happens, bug! + assert(false); + } // switch (typecode) + // moving to next container + it->container_index++; + return (it->has_value = loadfirstvalue(it)); +} + +bool roaring_previous_uint32_iterator(roaring_uint32_iterator_t *it) { + if (it->container_index < 0) { + return (it->has_value = false); + } + if (it->container_index >= it->parent->high_low_container.size) { + it->container_index = it->parent->high_low_container.size - 1; + return (it->has_value = loadlastvalue(it)); + } + + switch (it->typecode) { + case BITSET_CONTAINER_TYPE_CODE: { + if (--it->in_container_index < 0) + break; + + const bitset_container_t* bitset_container = (const bitset_container_t*)it->container; + int32_t wordindex = it->in_container_index / 64; + uint64_t word = bitset_container->array[wordindex] & (UINT64_MAX >> (63 - (it->in_container_index % 64))); + + while (word == 0 && --wordindex >= 0) { + word = bitset_container->array[wordindex]; + } + if (word == 0) + break; + + int num_leading_zeros = __builtin_clzll(word); + it->in_container_index = (wordindex * 64) + (63 - num_leading_zeros); + it->current_value = it->highbits | it->in_container_index; + return (it->has_value = true); + } + case ARRAY_CONTAINER_TYPE_CODE: { + if (--it->in_container_index < 0) + break; + + const array_container_t* array_container = (const array_container_t*)it->container; + it->current_value = it->highbits | array_container->array[it->in_container_index]; + return (it->has_value = true); + } + case RUN_CONTAINER_TYPE_CODE: { + if(it->current_value == 0) + return (it->has_value = false); + + const run_container_t* run_container = (const run_container_t*)it->container; + if (--it->current_value >= (it->highbits | run_container->runs[it->run_index].value)) { + return (it->has_value = true); + } + + if (--it->run_index < 0) + break; + + it->current_value = it->highbits | (run_container->runs[it->run_index].value + + run_container->runs[it->run_index].length); + return (it->has_value = true); + } + default: + // if this ever happens, bug! + assert(false); + } // switch (typecode) + + // moving to previous container + it->container_index--; + return (it->has_value = loadlastvalue(it)); +} + +uint32_t roaring_read_uint32_iterator(roaring_uint32_iterator_t *it, uint32_t* buf, uint32_t count) { + uint32_t ret = 0; + uint32_t num_values; + uint32_t wordindex; // used for bitsets + uint64_t word; // used for bitsets + const array_container_t* acont; //TODO remove + const run_container_t* rcont; //TODO remove + const bitset_container_t* bcont; //TODO remove + + while (it->has_value && ret < count) { + switch (it->typecode) { + case BITSET_CONTAINER_TYPE_CODE: + bcont = (const bitset_container_t*)(it->container); + wordindex = it->in_container_index / 64; + word = bcont->array[wordindex] & (UINT64_MAX << (it->in_container_index % 64)); + do { + while (word != 0 && ret < count) { + buf[0] = it->highbits | (wordindex * 64 + __builtin_ctzll(word)); + word = word & (word - 1); + buf++; + ret++; + } + while (word == 0 && wordindex+1 < BITSET_CONTAINER_SIZE_IN_WORDS) { + wordindex++; + word = bcont->array[wordindex]; + } + } while (word != 0 && ret < count); + it->has_value = (word != 0); + if (it->has_value) { + it->in_container_index = wordindex * 64 + __builtin_ctzll(word); + it->current_value = it->highbits | it->in_container_index; + } + break; + case ARRAY_CONTAINER_TYPE_CODE: + acont = (const array_container_t *)(it->container); + num_values = minimum_uint32(acont->cardinality - it->in_container_index, count - ret); + for (uint32_t i = 0; i < num_values; i++) { + buf[i] = it->highbits | acont->array[it->in_container_index + i]; + } + buf += num_values; + ret += num_values; + it->in_container_index += num_values; + it->has_value = (it->in_container_index < acont->cardinality); + if (it->has_value) { + it->current_value = it->highbits | acont->array[it->in_container_index]; + } + break; + case RUN_CONTAINER_TYPE_CODE: + rcont = (const run_container_t*)(it->container); + //"in_run_index" name is misleading, read it as "max_value_in_current_run" + do { + uint32_t largest_run_value = it->highbits | (rcont->runs[it->run_index].value + rcont->runs[it->run_index].length); + num_values = minimum_uint32(largest_run_value - it->current_value + 1, count - ret); + for (uint32_t i = 0; i < num_values; i++) { + buf[i] = it->current_value + i; + } + it->current_value += num_values; // this can overflow to zero: UINT32_MAX+1=0 + buf += num_values; + ret += num_values; + + if (it->current_value > largest_run_value || it->current_value == 0) { + it->run_index++; + if (it->run_index < rcont->n_runs) { + it->current_value = it->highbits | rcont->runs[it->run_index].value; + } else { + it->has_value = false; + } + } + } while ((ret < count) && it->has_value); + break; + default: + assert(false); + } + if (it->has_value) { + assert(ret == count); + return ret; + } + it->container_index++; + it->has_value = loadfirstvalue(it); + } + return ret; +} + + + +void roaring_free_uint32_iterator(roaring_uint32_iterator_t *it) { free(it); } + +/**** +* end of roaring_uint32_iterator_t +*****/ + +bool roaring_bitmap_equals(const roaring_bitmap_t *ra1, + const roaring_bitmap_t *ra2) { + if (ra1->high_low_container.size != ra2->high_low_container.size) { + return false; + } + for (int i = 0; i < ra1->high_low_container.size; ++i) { + if (ra1->high_low_container.keys[i] != + ra2->high_low_container.keys[i]) { + return false; + } + } + for (int i = 0; i < ra1->high_low_container.size; ++i) { + bool areequal = container_equals(ra1->high_low_container.containers[i], + ra1->high_low_container.typecodes[i], + ra2->high_low_container.containers[i], + ra2->high_low_container.typecodes[i]); + if (!areequal) { + return false; + } + } + return true; +} + +bool roaring_bitmap_is_subset(const roaring_bitmap_t *ra1, + const roaring_bitmap_t *ra2) { + const int length1 = ra1->high_low_container.size, + length2 = ra2->high_low_container.size; + + int pos1 = 0, pos2 = 0; + + while (pos1 < length1 && pos2 < length2) { + const uint16_t s1 = ra_get_key_at_index(&ra1->high_low_container, pos1); + const uint16_t s2 = ra_get_key_at_index(&ra2->high_low_container, pos2); + + if (s1 == s2) { + uint8_t container_type_1, container_type_2; + void *c1 = ra_get_container_at_index(&ra1->high_low_container, pos1, + &container_type_1); + void *c2 = ra_get_container_at_index(&ra2->high_low_container, pos2, + &container_type_2); + bool subset = + container_is_subset(c1, container_type_1, c2, container_type_2); + if (!subset) return false; + ++pos1; + ++pos2; + } else if (s1 < s2) { // s1 < s2 + return false; + } else { // s1 > s2 + pos2 = ra_advance_until(&ra2->high_low_container, s1, pos2); + } + } + if (pos1 == length1) + return true; + else + return false; +} + +static void insert_flipped_container(roaring_array_t *ans_arr, + const roaring_array_t *x1_arr, uint16_t hb, + uint16_t lb_start, uint16_t lb_end) { + const int i = ra_get_index(x1_arr, hb); + const int j = ra_get_index(ans_arr, hb); + uint8_t ctype_in, ctype_out; + void *flipped_container = NULL; + if (i >= 0) { + void *container_to_flip = + ra_get_container_at_index(x1_arr, i, &ctype_in); + flipped_container = + container_not_range(container_to_flip, ctype_in, (uint32_t)lb_start, + (uint32_t)(lb_end + 1), &ctype_out); + + if (container_get_cardinality(flipped_container, ctype_out)) + ra_insert_new_key_value_at(ans_arr, -j - 1, hb, flipped_container, + ctype_out); + else { + container_free(flipped_container, ctype_out); + } + } else { + flipped_container = container_range_of_ones( + (uint32_t)lb_start, (uint32_t)(lb_end + 1), &ctype_out); + ra_insert_new_key_value_at(ans_arr, -j - 1, hb, flipped_container, + ctype_out); + } +} + +static void inplace_flip_container(roaring_array_t *x1_arr, uint16_t hb, + uint16_t lb_start, uint16_t lb_end) { + const int i = ra_get_index(x1_arr, hb); + uint8_t ctype_in, ctype_out; + void *flipped_container = NULL; + if (i >= 0) { + void *container_to_flip = + ra_get_container_at_index(x1_arr, i, &ctype_in); + flipped_container = container_inot_range( + container_to_flip, ctype_in, (uint32_t)lb_start, + (uint32_t)(lb_end + 1), &ctype_out); + // if a new container was created, the old one was already freed + if (container_get_cardinality(flipped_container, ctype_out)) { + ra_set_container_at_index(x1_arr, i, flipped_container, ctype_out); + } else { + container_free(flipped_container, ctype_out); + ra_remove_at_index(x1_arr, i); + } + + } else { + flipped_container = container_range_of_ones( + (uint32_t)lb_start, (uint32_t)(lb_end + 1), &ctype_out); + ra_insert_new_key_value_at(x1_arr, -i - 1, hb, flipped_container, + ctype_out); + } +} + +static void insert_fully_flipped_container(roaring_array_t *ans_arr, + const roaring_array_t *x1_arr, + uint16_t hb) { + const int i = ra_get_index(x1_arr, hb); + const int j = ra_get_index(ans_arr, hb); + uint8_t ctype_in, ctype_out; + void *flipped_container = NULL; + if (i >= 0) { + void *container_to_flip = + ra_get_container_at_index(x1_arr, i, &ctype_in); + flipped_container = + container_not(container_to_flip, ctype_in, &ctype_out); + if (container_get_cardinality(flipped_container, ctype_out)) + ra_insert_new_key_value_at(ans_arr, -j - 1, hb, flipped_container, + ctype_out); + else { + container_free(flipped_container, ctype_out); + } + } else { + flipped_container = container_range_of_ones(0U, 0x10000U, &ctype_out); + ra_insert_new_key_value_at(ans_arr, -j - 1, hb, flipped_container, + ctype_out); + } +} + +static void inplace_fully_flip_container(roaring_array_t *x1_arr, uint16_t hb) { + const int i = ra_get_index(x1_arr, hb); + uint8_t ctype_in, ctype_out; + void *flipped_container = NULL; + if (i >= 0) { + void *container_to_flip = + ra_get_container_at_index(x1_arr, i, &ctype_in); + flipped_container = + container_inot(container_to_flip, ctype_in, &ctype_out); + + if (container_get_cardinality(flipped_container, ctype_out)) { + ra_set_container_at_index(x1_arr, i, flipped_container, ctype_out); + } else { + container_free(flipped_container, ctype_out); + ra_remove_at_index(x1_arr, i); + } + + } else { + flipped_container = container_range_of_ones(0U, 0x10000U, &ctype_out); + ra_insert_new_key_value_at(x1_arr, -i - 1, hb, flipped_container, + ctype_out); + } +} + +roaring_bitmap_t *roaring_bitmap_flip(const roaring_bitmap_t *x1, + uint64_t range_start, + uint64_t range_end) { + if (range_start >= range_end) { + return roaring_bitmap_copy(x1); + } + if(range_end >= UINT64_C(0x100000000)) { + range_end = UINT64_C(0x100000000); + } + + roaring_bitmap_t *ans = roaring_bitmap_create(); + roaring_bitmap_set_copy_on_write(ans, is_cow(x1)); + + uint16_t hb_start = (uint16_t)(range_start >> 16); + const uint16_t lb_start = (uint16_t)range_start; // & 0xFFFF; + uint16_t hb_end = (uint16_t)((range_end - 1) >> 16); + const uint16_t lb_end = (uint16_t)(range_end - 1); // & 0xFFFF; + + ra_append_copies_until(&ans->high_low_container, &x1->high_low_container, + hb_start, is_cow(x1)); + if (hb_start == hb_end) { + insert_flipped_container(&ans->high_low_container, + &x1->high_low_container, hb_start, lb_start, + lb_end); + } else { + // start and end containers are distinct + if (lb_start > 0) { + // handle first (partial) container + insert_flipped_container(&ans->high_low_container, + &x1->high_low_container, hb_start, + lb_start, 0xFFFF); + ++hb_start; // for the full containers. Can't wrap. + } + + if (lb_end != 0xFFFF) --hb_end; // later we'll handle the partial block + + for (uint32_t hb = hb_start; hb <= hb_end; ++hb) { + insert_fully_flipped_container(&ans->high_low_container, + &x1->high_low_container, hb); + } + + // handle a partial final container + if (lb_end != 0xFFFF) { + insert_flipped_container(&ans->high_low_container, + &x1->high_low_container, hb_end + 1, 0, + lb_end); + ++hb_end; + } + } + ra_append_copies_after(&ans->high_low_container, &x1->high_low_container, + hb_end, is_cow(x1)); + return ans; +} + +void roaring_bitmap_flip_inplace(roaring_bitmap_t *x1, uint64_t range_start, + uint64_t range_end) { + if (range_start >= range_end) { + return; // empty range + } + if(range_end >= UINT64_C(0x100000000)) { + range_end = UINT64_C(0x100000000); + } + + uint16_t hb_start = (uint16_t)(range_start >> 16); + const uint16_t lb_start = (uint16_t)range_start; + uint16_t hb_end = (uint16_t)((range_end - 1) >> 16); + const uint16_t lb_end = (uint16_t)(range_end - 1); + + if (hb_start == hb_end) { + inplace_flip_container(&x1->high_low_container, hb_start, lb_start, + lb_end); + } else { + // start and end containers are distinct + if (lb_start > 0) { + // handle first (partial) container + inplace_flip_container(&x1->high_low_container, hb_start, lb_start, + 0xFFFF); + ++hb_start; // for the full containers. Can't wrap. + } + + if (lb_end != 0xFFFF) --hb_end; + + for (uint32_t hb = hb_start; hb <= hb_end; ++hb) { + inplace_fully_flip_container(&x1->high_low_container, hb); + } + // handle a partial final container + if (lb_end != 0xFFFF) { + inplace_flip_container(&x1->high_low_container, hb_end + 1, 0, + lb_end); + ++hb_end; + } + } +} + +roaring_bitmap_t *roaring_bitmap_lazy_or(const roaring_bitmap_t *x1, + const roaring_bitmap_t *x2, + const bool bitsetconversion) { + uint8_t container_result_type = 0; + const int length1 = x1->high_low_container.size, + length2 = x2->high_low_container.size; + if (0 == length1) { + return roaring_bitmap_copy(x2); + } + if (0 == length2) { + return roaring_bitmap_copy(x1); + } + roaring_bitmap_t *answer = + roaring_bitmap_create_with_capacity(length1 + length2); + roaring_bitmap_set_copy_on_write(answer, is_cow(x1) && is_cow(x2)); + int pos1 = 0, pos2 = 0; + uint8_t container_type_1, container_type_2; + uint16_t s1 = ra_get_key_at_index(&x1->high_low_container, pos1); + uint16_t s2 = ra_get_key_at_index(&x2->high_low_container, pos2); + while (true) { + if (s1 == s2) { + void *c1 = ra_get_container_at_index(&x1->high_low_container, pos1, + &container_type_1); + void *c2 = ra_get_container_at_index(&x2->high_low_container, pos2, + &container_type_2); + void *c; + if (bitsetconversion && (get_container_type(c1, container_type_1) != + BITSET_CONTAINER_TYPE_CODE) && + (get_container_type(c2, container_type_2) != + BITSET_CONTAINER_TYPE_CODE)) { + void *newc1 = + container_mutable_unwrap_shared(c1, &container_type_1); + newc1 = container_to_bitset(newc1, container_type_1); + container_type_1 = BITSET_CONTAINER_TYPE_CODE; + c = container_lazy_ior(newc1, container_type_1, c2, + container_type_2, + &container_result_type); + if (c != newc1) { // should not happen + container_free(newc1, container_type_1); + } + } else { + c = container_lazy_or(c1, container_type_1, c2, + container_type_2, &container_result_type); + } + // since we assume that the initial containers are non-empty, + // the + // result here + // can only be non-empty + ra_append(&answer->high_low_container, s1, c, + container_result_type); + ++pos1; + ++pos2; + if (pos1 == length1) break; + if (pos2 == length2) break; + s1 = ra_get_key_at_index(&x1->high_low_container, pos1); + s2 = ra_get_key_at_index(&x2->high_low_container, pos2); + + } else if (s1 < s2) { // s1 < s2 + void *c1 = ra_get_container_at_index(&x1->high_low_container, pos1, + &container_type_1); + c1 = + get_copy_of_container(c1, &container_type_1, is_cow(x1)); + if (is_cow(x1)) { + ra_set_container_at_index(&x1->high_low_container, pos1, c1, + container_type_1); + } + ra_append(&answer->high_low_container, s1, c1, container_type_1); + pos1++; + if (pos1 == length1) break; + s1 = ra_get_key_at_index(&x1->high_low_container, pos1); + + } else { // s1 > s2 + void *c2 = ra_get_container_at_index(&x2->high_low_container, pos2, + &container_type_2); + c2 = + get_copy_of_container(c2, &container_type_2, is_cow(x2)); + if (is_cow(x2)) { + ra_set_container_at_index(&x2->high_low_container, pos2, c2, + container_type_2); + } + ra_append(&answer->high_low_container, s2, c2, container_type_2); + pos2++; + if (pos2 == length2) break; + s2 = ra_get_key_at_index(&x2->high_low_container, pos2); + } + } + if (pos1 == length1) { + ra_append_copy_range(&answer->high_low_container, + &x2->high_low_container, pos2, length2, + is_cow(x2)); + } else if (pos2 == length2) { + ra_append_copy_range(&answer->high_low_container, + &x1->high_low_container, pos1, length1, + is_cow(x1)); + } + return answer; +} + +void roaring_bitmap_lazy_or_inplace(roaring_bitmap_t *x1, + const roaring_bitmap_t *x2, + const bool bitsetconversion) { + uint8_t container_result_type = 0; + int length1 = x1->high_low_container.size; + const int length2 = x2->high_low_container.size; + + if (0 == length2) return; + + if (0 == length1) { + roaring_bitmap_overwrite(x1, x2); + return; + } + int pos1 = 0, pos2 = 0; + uint8_t container_type_1, container_type_2; + uint16_t s1 = ra_get_key_at_index(&x1->high_low_container, pos1); + uint16_t s2 = ra_get_key_at_index(&x2->high_low_container, pos2); + while (true) { + if (s1 == s2) { + void *c1 = ra_get_container_at_index(&x1->high_low_container, pos1, + &container_type_1); + if (!container_is_full(c1, container_type_1)) { + if ((bitsetconversion == false) || + (get_container_type(c1, container_type_1) == + BITSET_CONTAINER_TYPE_CODE)) { + c1 = get_writable_copy_if_shared(c1, &container_type_1); + } else { + // convert to bitset + void *oldc1 = c1; + uint8_t oldt1 = container_type_1; + c1 = container_mutable_unwrap_shared(c1, &container_type_1); + c1 = container_to_bitset(c1, container_type_1); + container_free(oldc1, oldt1); + container_type_1 = BITSET_CONTAINER_TYPE_CODE; + } + + void *c2 = ra_get_container_at_index(&x2->high_low_container, + pos2, &container_type_2); + void *c = container_lazy_ior(c1, container_type_1, c2, + container_type_2, + &container_result_type); + if (c != + c1) { // in this instance a new container was created, and + // we need to free the old one + container_free(c1, container_type_1); + } + + ra_set_container_at_index(&x1->high_low_container, pos1, c, + container_result_type); + } + ++pos1; + ++pos2; + if (pos1 == length1) break; + if (pos2 == length2) break; + s1 = ra_get_key_at_index(&x1->high_low_container, pos1); + s2 = ra_get_key_at_index(&x2->high_low_container, pos2); + + } else if (s1 < s2) { // s1 < s2 + pos1++; + if (pos1 == length1) break; + s1 = ra_get_key_at_index(&x1->high_low_container, pos1); + + } else { // s1 > s2 + void *c2 = ra_get_container_at_index(&x2->high_low_container, pos2, + &container_type_2); + // void *c2_clone = container_clone(c2, container_type_2); + c2 = + get_copy_of_container(c2, &container_type_2, is_cow(x2)); + if (is_cow(x2)) { + ra_set_container_at_index(&x2->high_low_container, pos2, c2, + container_type_2); + } + ra_insert_new_key_value_at(&x1->high_low_container, pos1, s2, c2, + container_type_2); + pos1++; + length1++; + pos2++; + if (pos2 == length2) break; + s2 = ra_get_key_at_index(&x2->high_low_container, pos2); + } + } + if (pos1 == length1) { + ra_append_copy_range(&x1->high_low_container, &x2->high_low_container, + pos2, length2, is_cow(x2)); + } +} + +roaring_bitmap_t *roaring_bitmap_lazy_xor(const roaring_bitmap_t *x1, + const roaring_bitmap_t *x2) { + uint8_t container_result_type = 0; + const int length1 = x1->high_low_container.size, + length2 = x2->high_low_container.size; + if (0 == length1) { + return roaring_bitmap_copy(x2); + } + if (0 == length2) { + return roaring_bitmap_copy(x1); + } + roaring_bitmap_t *answer = + roaring_bitmap_create_with_capacity(length1 + length2); + roaring_bitmap_set_copy_on_write(answer, is_cow(x1) && is_cow(x2)); + int pos1 = 0, pos2 = 0; + uint8_t container_type_1, container_type_2; + uint16_t s1 = ra_get_key_at_index(&x1->high_low_container, pos1); + uint16_t s2 = ra_get_key_at_index(&x2->high_low_container, pos2); + while (true) { + if (s1 == s2) { + void *c1 = ra_get_container_at_index(&x1->high_low_container, pos1, + &container_type_1); + void *c2 = ra_get_container_at_index(&x2->high_low_container, pos2, + &container_type_2); + void *c = + container_lazy_xor(c1, container_type_1, c2, container_type_2, + &container_result_type); + + if (container_nonzero_cardinality(c, container_result_type)) { + ra_append(&answer->high_low_container, s1, c, + container_result_type); + } else { + container_free(c, container_result_type); + } + + ++pos1; + ++pos2; + if (pos1 == length1) break; + if (pos2 == length2) break; + s1 = ra_get_key_at_index(&x1->high_low_container, pos1); + s2 = ra_get_key_at_index(&x2->high_low_container, pos2); + + } else if (s1 < s2) { // s1 < s2 + void *c1 = ra_get_container_at_index(&x1->high_low_container, pos1, + &container_type_1); + c1 = + get_copy_of_container(c1, &container_type_1, is_cow(x1)); + if (is_cow(x1)) { + ra_set_container_at_index(&x1->high_low_container, pos1, c1, + container_type_1); + } + ra_append(&answer->high_low_container, s1, c1, container_type_1); + pos1++; + if (pos1 == length1) break; + s1 = ra_get_key_at_index(&x1->high_low_container, pos1); + + } else { // s1 > s2 + void *c2 = ra_get_container_at_index(&x2->high_low_container, pos2, + &container_type_2); + c2 = + get_copy_of_container(c2, &container_type_2, is_cow(x2)); + if (is_cow(x2)) { + ra_set_container_at_index(&x2->high_low_container, pos2, c2, + container_type_2); + } + ra_append(&answer->high_low_container, s2, c2, container_type_2); + pos2++; + if (pos2 == length2) break; + s2 = ra_get_key_at_index(&x2->high_low_container, pos2); + } + } + if (pos1 == length1) { + ra_append_copy_range(&answer->high_low_container, + &x2->high_low_container, pos2, length2, + is_cow(x2)); + } else if (pos2 == length2) { + ra_append_copy_range(&answer->high_low_container, + &x1->high_low_container, pos1, length1, + is_cow(x1)); + } + return answer; +} + +void roaring_bitmap_lazy_xor_inplace(roaring_bitmap_t *x1, + const roaring_bitmap_t *x2) { + assert(x1 != x2); + uint8_t container_result_type = 0; + int length1 = x1->high_low_container.size; + const int length2 = x2->high_low_container.size; + + if (0 == length2) return; + + if (0 == length1) { + roaring_bitmap_overwrite(x1, x2); + return; + } + int pos1 = 0, pos2 = 0; + uint8_t container_type_1, container_type_2; + uint16_t s1 = ra_get_key_at_index(&x1->high_low_container, pos1); + uint16_t s2 = ra_get_key_at_index(&x2->high_low_container, pos2); + while (true) { + if (s1 == s2) { + void *c1 = ra_get_container_at_index(&x1->high_low_container, pos1, + &container_type_1); + c1 = get_writable_copy_if_shared(c1, &container_type_1); + void *c2 = ra_get_container_at_index(&x2->high_low_container, pos2, + &container_type_2); + void *c = + container_lazy_ixor(c1, container_type_1, c2, container_type_2, + &container_result_type); + if (container_nonzero_cardinality(c, container_result_type)) { + ra_set_container_at_index(&x1->high_low_container, pos1, c, + container_result_type); + ++pos1; + } else { + container_free(c, container_result_type); + ra_remove_at_index(&x1->high_low_container, pos1); + --length1; + } + ++pos2; + if (pos1 == length1) break; + if (pos2 == length2) break; + s1 = ra_get_key_at_index(&x1->high_low_container, pos1); + s2 = ra_get_key_at_index(&x2->high_low_container, pos2); + + } else if (s1 < s2) { // s1 < s2 + pos1++; + if (pos1 == length1) break; + s1 = ra_get_key_at_index(&x1->high_low_container, pos1); + + } else { // s1 > s2 + void *c2 = ra_get_container_at_index(&x2->high_low_container, pos2, + &container_type_2); + // void *c2_clone = container_clone(c2, container_type_2); + c2 = + get_copy_of_container(c2, &container_type_2, is_cow(x2)); + if (is_cow(x2)) { + ra_set_container_at_index(&x2->high_low_container, pos2, c2, + container_type_2); + } + ra_insert_new_key_value_at(&x1->high_low_container, pos1, s2, c2, + container_type_2); + pos1++; + length1++; + pos2++; + if (pos2 == length2) break; + s2 = ra_get_key_at_index(&x2->high_low_container, pos2); + } + } + if (pos1 == length1) { + ra_append_copy_range(&x1->high_low_container, &x2->high_low_container, + pos2, length2, is_cow(x2)); + } +} + +void roaring_bitmap_repair_after_lazy(roaring_bitmap_t *ra) { + for (int i = 0; i < ra->high_low_container.size; ++i) { + const uint8_t original_typecode = ra->high_low_container.typecodes[i]; + void *container = ra->high_low_container.containers[i]; + uint8_t new_typecode = original_typecode; + void *newcontainer = + container_repair_after_lazy(container, &new_typecode); + ra->high_low_container.containers[i] = newcontainer; + ra->high_low_container.typecodes[i] = new_typecode; + } +} + + + +/** +* roaring_bitmap_rank returns the number of integers that are smaller or equal +* to x. +*/ +uint64_t roaring_bitmap_rank(const roaring_bitmap_t *bm, uint32_t x) { + uint64_t size = 0; + uint32_t xhigh = x >> 16; + for (int i = 0; i < bm->high_low_container.size; i++) { + uint32_t key = bm->high_low_container.keys[i]; + if (xhigh > key) { + size += + container_get_cardinality(bm->high_low_container.containers[i], + bm->high_low_container.typecodes[i]); + } else if (xhigh == key) { + return size + container_rank(bm->high_low_container.containers[i], + bm->high_low_container.typecodes[i], + x & 0xFFFF); + } else { + return size; + } + } + return size; +} + +/** +* roaring_bitmap_smallest returns the smallest value in the set. +* Returns UINT32_MAX if the set is empty. +*/ +uint32_t roaring_bitmap_minimum(const roaring_bitmap_t *bm) { + if (bm->high_low_container.size > 0) { + void *container = bm->high_low_container.containers[0]; + uint8_t typecode = bm->high_low_container.typecodes[0]; + uint32_t key = bm->high_low_container.keys[0]; + uint32_t lowvalue = container_minimum(container, typecode); + return lowvalue | (key << 16); + } + return UINT32_MAX; +} + +/** +* roaring_bitmap_smallest returns the greatest value in the set. +* Returns 0 if the set is empty. +*/ +uint32_t roaring_bitmap_maximum(const roaring_bitmap_t *bm) { + if (bm->high_low_container.size > 0) { + void *container = + bm->high_low_container.containers[bm->high_low_container.size - 1]; + uint8_t typecode = + bm->high_low_container.typecodes[bm->high_low_container.size - 1]; + uint32_t key = + bm->high_low_container.keys[bm->high_low_container.size - 1]; + uint32_t lowvalue = container_maximum(container, typecode); + return lowvalue | (key << 16); + } + return 0; +} + +bool roaring_bitmap_select(const roaring_bitmap_t *bm, uint32_t rank, + uint32_t *element) { + void *container; + uint8_t typecode; + uint16_t key; + uint32_t start_rank = 0; + int i = 0; + bool valid = false; + while (!valid && i < bm->high_low_container.size) { + container = bm->high_low_container.containers[i]; + typecode = bm->high_low_container.typecodes[i]; + valid = + container_select(container, typecode, &start_rank, rank, element); + i++; + } + + if (valid) { + key = bm->high_low_container.keys[i - 1]; + *element |= (key << 16); + return true; + } else + return false; +} + +bool roaring_bitmap_intersect(const roaring_bitmap_t *x1, + const roaring_bitmap_t *x2) { + const int length1 = x1->high_low_container.size, + length2 = x2->high_low_container.size; + uint64_t answer = 0; + int pos1 = 0, pos2 = 0; + + while (pos1 < length1 && pos2 < length2) { + const uint16_t s1 = ra_get_key_at_index(& x1->high_low_container, pos1); + const uint16_t s2 = ra_get_key_at_index(& x2->high_low_container, pos2); + + if (s1 == s2) { + uint8_t container_type_1, container_type_2; + void *c1 = ra_get_container_at_index(& x1->high_low_container, pos1, + &container_type_1); + void *c2 = ra_get_container_at_index(& x2->high_low_container, pos2, + &container_type_2); + if( container_intersect(c1, container_type_1, c2, container_type_2) ) return true; + ++pos1; + ++pos2; + } else if (s1 < s2) { // s1 < s2 + pos1 = ra_advance_until(& x1->high_low_container, s2, pos1); + } else { // s1 > s2 + pos2 = ra_advance_until(& x2->high_low_container, s1, pos2); + } + } + return answer; +} + + +uint64_t roaring_bitmap_and_cardinality(const roaring_bitmap_t *x1, + const roaring_bitmap_t *x2) { + const int length1 = x1->high_low_container.size, + length2 = x2->high_low_container.size; + uint64_t answer = 0; + int pos1 = 0, pos2 = 0; + + while (pos1 < length1 && pos2 < length2) { + const uint16_t s1 = ra_get_key_at_index(&x1->high_low_container, pos1); + const uint16_t s2 = ra_get_key_at_index(&x2->high_low_container, pos2); + + if (s1 == s2) { + uint8_t container_type_1, container_type_2; + void *c1 = ra_get_container_at_index(&x1->high_low_container, pos1, + &container_type_1); + void *c2 = ra_get_container_at_index(&x2->high_low_container, pos2, + &container_type_2); + answer += container_and_cardinality(c1, container_type_1, c2, + container_type_2); + ++pos1; + ++pos2; + } else if (s1 < s2) { // s1 < s2 + pos1 = ra_advance_until(&x1->high_low_container, s2, pos1); + } else { // s1 > s2 + pos2 = ra_advance_until(&x2->high_low_container, s1, pos2); + } + } + return answer; +} + +double roaring_bitmap_jaccard_index(const roaring_bitmap_t *x1, + const roaring_bitmap_t *x2) { + const uint64_t c1 = roaring_bitmap_get_cardinality(x1); + const uint64_t c2 = roaring_bitmap_get_cardinality(x2); + const uint64_t inter = roaring_bitmap_and_cardinality(x1, x2); + return (double)inter / (double)(c1 + c2 - inter); +} + +uint64_t roaring_bitmap_or_cardinality(const roaring_bitmap_t *x1, + const roaring_bitmap_t *x2) { + const uint64_t c1 = roaring_bitmap_get_cardinality(x1); + const uint64_t c2 = roaring_bitmap_get_cardinality(x2); + const uint64_t inter = roaring_bitmap_and_cardinality(x1, x2); + return c1 + c2 - inter; +} + +uint64_t roaring_bitmap_andnot_cardinality(const roaring_bitmap_t *x1, + const roaring_bitmap_t *x2) { + const uint64_t c1 = roaring_bitmap_get_cardinality(x1); + const uint64_t inter = roaring_bitmap_and_cardinality(x1, x2); + return c1 - inter; +} + +uint64_t roaring_bitmap_xor_cardinality(const roaring_bitmap_t *x1, + const roaring_bitmap_t *x2) { + const uint64_t c1 = roaring_bitmap_get_cardinality(x1); + const uint64_t c2 = roaring_bitmap_get_cardinality(x2); + const uint64_t inter = roaring_bitmap_and_cardinality(x1, x2); + return c1 + c2 - 2 * inter; +} + + +/** + * Check whether a range of values from range_start (included) to range_end (excluded) is present + */ +bool roaring_bitmap_contains_range(const roaring_bitmap_t *r, uint64_t range_start, uint64_t range_end) { + if(range_end >= UINT64_C(0x100000000)) { + range_end = UINT64_C(0x100000000); + } + if (range_start >= range_end) return true; // empty range are always contained! + if (range_end - range_start == 1) return roaring_bitmap_contains(r, (uint32_t)range_start); + uint16_t hb_rs = (uint16_t)(range_start >> 16); + uint16_t hb_re = (uint16_t)((range_end - 1) >> 16); + const int32_t span = hb_re - hb_rs; + const int32_t hlc_sz = ra_get_size(&r->high_low_container); + if (hlc_sz < span + 1) { + return false; + } + int32_t is = ra_get_index(&r->high_low_container, hb_rs); + int32_t ie = ra_get_index(&r->high_low_container, hb_re); + ie = (ie < 0 ? -ie - 1 : ie); + if ((is < 0) || ((ie - is) != span)) { + return false; + } + const uint32_t lb_rs = range_start & 0xFFFF; + const uint32_t lb_re = ((range_end - 1) & 0xFFFF) + 1; + uint8_t typecode; + void *container = ra_get_container_at_index(&r->high_low_container, is, &typecode); + if (hb_rs == hb_re) { + return container_contains_range(container, lb_rs, lb_re, typecode); + } + if (!container_contains_range(container, lb_rs, 1 << 16, typecode)) { + return false; + } + assert(ie < hlc_sz); // would indicate an algorithmic bug + container = ra_get_container_at_index(&r->high_low_container, ie, &typecode); + if (!container_contains_range(container, 0, lb_re, typecode)) { + return false; + } + for (int32_t i = is + 1; i < ie; ++i) { + container = ra_get_container_at_index(&r->high_low_container, i, &typecode); + if (!container_is_full(container, typecode) ) { + return false; + } + } + return true; +} + + +bool roaring_bitmap_is_strict_subset(const roaring_bitmap_t *ra1, + const roaring_bitmap_t *ra2) { + return (roaring_bitmap_get_cardinality(ra2) > + roaring_bitmap_get_cardinality(ra1) && + roaring_bitmap_is_subset(ra1, ra2)); +} + + +/* + * FROZEN SERIALIZATION FORMAT DESCRIPTION + * + * -- (beginning must be aligned by 32 bytes) -- + * uint64_t[BITSET_CONTAINER_SIZE_IN_WORDS * num_bitset_containers] + * rle16_t[total number of rle elements in all run containers] + * uint16_t[total number of array elements in all array containers] + * uint16_t[num_containers] + * uint16_t[num_containers] + * uint8_t[num_containers] + *
uint32_t + * + *
is a 4-byte value which is a bit union of FROZEN_COOKIE (15 bits) + * and the number of containers (17 bits). + * + * stores number of elements for every container. + * Its meaning depends on container type. + * For array and bitset containers, this value is the container cardinality minus one. + * For run container, it is the number of rle_t elements (n_runs). + * + * ,, are flat arrays of elements of + * all containers of respective type. + * + * <*_data> and are kept close together because they are not accessed + * during deserilization. This may reduce IO in case of large mapped bitmaps. + * All members have their native alignments during deserilization except
, + * which is not guaranteed to be aligned by 4 bytes. + */ + +size_t roaring_bitmap_frozen_size_in_bytes(const roaring_bitmap_t *rb) { + const roaring_array_t *ra = &rb->high_low_container; + size_t num_bytes = 0; + for (int32_t i = 0; i < ra->size; i++) { + switch (ra->typecodes[i]) { + case BITSET_CONTAINER_TYPE_CODE: { + num_bytes += BITSET_CONTAINER_SIZE_IN_WORDS * sizeof(uint64_t); + break; + } + case RUN_CONTAINER_TYPE_CODE: { + const run_container_t *run = + (const run_container_t *) ra->containers[i]; + num_bytes += run->n_runs * sizeof(rle16_t); + break; + } + case ARRAY_CONTAINER_TYPE_CODE: { + const array_container_t *array = + (const array_container_t *) ra->containers[i]; + num_bytes += array->cardinality * sizeof(uint16_t); + break; + } + default: + __builtin_unreachable(); + } + } + num_bytes += (2 + 2 + 1) * ra->size; // keys, counts, typecodes + num_bytes += 4; // header + return num_bytes; +} + +inline static void *arena_alloc(char **arena, size_t num_bytes) { + char *res = *arena; + *arena += num_bytes; + return res; +} + +void roaring_bitmap_frozen_serialize(const roaring_bitmap_t *rb, char *buf) { + /* + * Note: we do not require user to supply spicificly aligned buffer. + * Thus we have to use memcpy() everywhere. + */ + + const roaring_array_t *ra = &rb->high_low_container; + + size_t bitset_zone_size = 0; + size_t run_zone_size = 0; + size_t array_zone_size = 0; + for (int32_t i = 0; i < ra->size; i++) { + switch (ra->typecodes[i]) { + case BITSET_CONTAINER_TYPE_CODE: { + bitset_zone_size += + BITSET_CONTAINER_SIZE_IN_WORDS * sizeof(uint64_t); + break; + } + case RUN_CONTAINER_TYPE_CODE: { + const run_container_t *run = + (const run_container_t *) ra->containers[i]; + run_zone_size += run->n_runs * sizeof(rle16_t); + break; + } + case ARRAY_CONTAINER_TYPE_CODE: { + const array_container_t *array = + (const array_container_t *) ra->containers[i]; + array_zone_size += array->cardinality * sizeof(uint16_t); + break; + } + default: + __builtin_unreachable(); + } + } + + uint64_t *bitset_zone = (uint64_t *)arena_alloc(&buf, bitset_zone_size); + rle16_t *run_zone = (rle16_t *)arena_alloc(&buf, run_zone_size); + uint16_t *array_zone = (uint16_t *)arena_alloc(&buf, array_zone_size); + uint16_t *key_zone = (uint16_t *)arena_alloc(&buf, 2*ra->size); + uint16_t *count_zone = (uint16_t *)arena_alloc(&buf, 2*ra->size); + uint8_t *typecode_zone = (uint8_t *)arena_alloc(&buf, ra->size); + uint32_t *header_zone = (uint32_t *)arena_alloc(&buf, 4); + + for (int32_t i = 0; i < ra->size; i++) { + uint16_t count; + switch (ra->typecodes[i]) { + case BITSET_CONTAINER_TYPE_CODE: { + const bitset_container_t *bitset = + (const bitset_container_t *) ra->containers[i]; + memcpy(bitset_zone, bitset->array, + BITSET_CONTAINER_SIZE_IN_WORDS * sizeof(uint64_t)); + bitset_zone += BITSET_CONTAINER_SIZE_IN_WORDS; + if (bitset->cardinality != BITSET_UNKNOWN_CARDINALITY) { + count = bitset->cardinality - 1; + } else { + count = bitset_container_compute_cardinality(bitset) - 1; + } + break; + } + case RUN_CONTAINER_TYPE_CODE: { + const run_container_t *run = + (const run_container_t *) ra->containers[i]; + size_t num_bytes = run->n_runs * sizeof(rle16_t); + memcpy(run_zone, run->runs, num_bytes); + run_zone += run->n_runs; + count = run->n_runs; + break; + } + case ARRAY_CONTAINER_TYPE_CODE: { + const array_container_t *array = + (const array_container_t *) ra->containers[i]; + size_t num_bytes = array->cardinality * sizeof(uint16_t); + memcpy(array_zone, array->array, num_bytes); + array_zone += array->cardinality; + count = array->cardinality - 1; + break; + } + default: + __builtin_unreachable(); + } + memcpy(&count_zone[i], &count, 2); + } + memcpy(key_zone, ra->keys, ra->size * sizeof(uint16_t)); + memcpy(typecode_zone, ra->typecodes, ra->size * sizeof(uint8_t)); + uint32_t header = ((uint32_t)ra->size << 15) | FROZEN_COOKIE; + memcpy(header_zone, &header, 4); +} + +const roaring_bitmap_t * +roaring_bitmap_frozen_view(const char *buf, size_t length) { + if ((uintptr_t)buf % 32 != 0) { + return NULL; + } + + // cookie and num_containers + if (length < 4) { + return NULL; + } + uint32_t header; + memcpy(&header, buf + length - 4, 4); // header may be misaligned + if ((header & 0x7FFF) != FROZEN_COOKIE) { + return NULL; + } + int32_t num_containers = (header >> 15); + + // typecodes, counts and keys + if (length < 4 + (size_t)num_containers * (1 + 2 + 2)) { + return NULL; + } + uint16_t *keys = (uint16_t *)(buf + length - 4 - num_containers * 5); + uint16_t *counts = (uint16_t *)(buf + length - 4 - num_containers * 3); + uint8_t *typecodes = (uint8_t *)(buf + length - 4 - num_containers * 1); + + // {bitset,array,run}_zone + int32_t num_bitset_containers = 0; + int32_t num_run_containers = 0; + int32_t num_array_containers = 0; + size_t bitset_zone_size = 0; + size_t run_zone_size = 0; + size_t array_zone_size = 0; + for (int32_t i = 0; i < num_containers; i++) { + switch (typecodes[i]) { + case BITSET_CONTAINER_TYPE_CODE: + num_bitset_containers++; + bitset_zone_size += BITSET_CONTAINER_SIZE_IN_WORDS * sizeof(uint64_t); + break; + case RUN_CONTAINER_TYPE_CODE: + num_run_containers++; + run_zone_size += counts[i] * sizeof(rle16_t); + break; + case ARRAY_CONTAINER_TYPE_CODE: + num_array_containers++; + array_zone_size += (counts[i] + UINT32_C(1)) * sizeof(uint16_t); + break; + default: + return NULL; + } + } + if (length != bitset_zone_size + run_zone_size + array_zone_size + + 5 * num_containers + 4) { + return NULL; + } + uint64_t *bitset_zone = (uint64_t*) (buf); + rle16_t *run_zone = (rle16_t*) (buf + bitset_zone_size); + uint16_t *array_zone = (uint16_t*) (buf + bitset_zone_size + run_zone_size); + + size_t alloc_size = 0; + alloc_size += sizeof(roaring_bitmap_t); + alloc_size += num_containers * sizeof(void *); + alloc_size += num_bitset_containers * sizeof(bitset_container_t); + alloc_size += num_run_containers * sizeof(run_container_t); + alloc_size += num_array_containers * sizeof(array_container_t); + + char *arena = (char *)malloc(alloc_size); + if (arena == NULL) { + return NULL; + } + + roaring_bitmap_t *rb = (roaring_bitmap_t *) + arena_alloc(&arena, sizeof(roaring_bitmap_t)); + rb->high_low_container.flags = ROARING_FLAG_FROZEN; + rb->high_low_container.allocation_size = num_containers; + rb->high_low_container.size = num_containers; + rb->high_low_container.keys = (uint16_t *)keys; + rb->high_low_container.typecodes = (uint8_t *)typecodes; + rb->high_low_container.containers = + (void **)arena_alloc(&arena, sizeof(void*) * num_containers); + for (int32_t i = 0; i < num_containers; i++) { + switch (typecodes[i]) { + case BITSET_CONTAINER_TYPE_CODE: { + bitset_container_t *bitset = (bitset_container_t *) + arena_alloc(&arena, sizeof(bitset_container_t)); + bitset->array = bitset_zone; + bitset->cardinality = counts[i] + UINT32_C(1); + rb->high_low_container.containers[i] = bitset; + bitset_zone += BITSET_CONTAINER_SIZE_IN_WORDS; + break; + } + case RUN_CONTAINER_TYPE_CODE: { + run_container_t *run = (run_container_t *) + arena_alloc(&arena, sizeof(run_container_t)); + run->capacity = counts[i]; + run->n_runs = counts[i]; + run->runs = run_zone; + rb->high_low_container.containers[i] = run; + run_zone += run->n_runs; + break; + } + case ARRAY_CONTAINER_TYPE_CODE: { + array_container_t *array = (array_container_t *) + arena_alloc(&arena, sizeof(array_container_t)); + array->capacity = counts[i] + UINT32_C(1); + array->cardinality = counts[i] + UINT32_C(1); + array->array = array_zone; + rb->high_low_container.containers[i] = array; + array_zone += counts[i] + UINT32_C(1); + break; + } + default: + free(arena); + return NULL; + } + } + + return rb; +} +/* end file src/roaring.c */ +/* begin file src/roaring_array.c */ +#include +#include +#include +#include +#include +#include + + +// Convention: [0,ra->size) all elements are initialized +// [ra->size, ra->allocation_size) is junk and contains nothing needing freeing + +static bool realloc_array(roaring_array_t *ra, int32_t new_capacity) { + // because we combine the allocations, it is not possible to use realloc + /*ra->keys = + (uint16_t *)realloc(ra->keys, sizeof(uint16_t) * new_capacity); +ra->containers = + (void **)realloc(ra->containers, sizeof(void *) * new_capacity); +ra->typecodes = + (uint8_t *)realloc(ra->typecodes, sizeof(uint8_t) * new_capacity); +if (!ra->keys || !ra->containers || !ra->typecodes) { + free(ra->keys); + free(ra->containers); + free(ra->typecodes); + return false; +}*/ + + if ( new_capacity == 0 ) { + free(ra->containers); + ra->containers = NULL; + ra->keys = NULL; + ra->typecodes = NULL; + ra->allocation_size = 0; + return true; + } + const size_t memoryneeded = + new_capacity * (sizeof(uint16_t) + sizeof(void *) + sizeof(uint8_t)); + void *bigalloc = malloc(memoryneeded); + if (!bigalloc) return false; + void *oldbigalloc = ra->containers; + void **newcontainers = (void **)bigalloc; + uint16_t *newkeys = (uint16_t *)(newcontainers + new_capacity); + uint8_t *newtypecodes = (uint8_t *)(newkeys + new_capacity); + assert((char *)(newtypecodes + new_capacity) == + (char *)bigalloc + memoryneeded); + if(ra->size > 0) { + memcpy(newcontainers, ra->containers, sizeof(void *) * ra->size); + memcpy(newkeys, ra->keys, sizeof(uint16_t) * ra->size); + memcpy(newtypecodes, ra->typecodes, sizeof(uint8_t) * ra->size); + } + ra->containers = newcontainers; + ra->keys = newkeys; + ra->typecodes = newtypecodes; + ra->allocation_size = new_capacity; + free(oldbigalloc); + return true; +} + +bool ra_init_with_capacity(roaring_array_t *new_ra, uint32_t cap) { + if (!new_ra) return false; + ra_init(new_ra); + + if (cap > INT32_MAX) { return false; } + + if(cap > 0) { + void *bigalloc = + malloc(cap * (sizeof(uint16_t) + sizeof(void *) + sizeof(uint8_t))); + if( bigalloc == NULL ) return false; + new_ra->containers = (void **)bigalloc; + new_ra->keys = (uint16_t *)(new_ra->containers + cap); + new_ra->typecodes = (uint8_t *)(new_ra->keys + cap); + // Narrowing is safe because of above check + new_ra->allocation_size = (int32_t)cap; + } + return true; +} + +int ra_shrink_to_fit(roaring_array_t *ra) { + int savings = (ra->allocation_size - ra->size) * + (sizeof(uint16_t) + sizeof(void *) + sizeof(uint8_t)); + if (!realloc_array(ra, ra->size)) { + return 0; + } + ra->allocation_size = ra->size; + return savings; +} + +void ra_init(roaring_array_t *new_ra) { + if (!new_ra) { return; } + new_ra->keys = NULL; + new_ra->containers = NULL; + new_ra->typecodes = NULL; + + new_ra->allocation_size = 0; + new_ra->size = 0; + new_ra->flags = 0; +} + +bool ra_copy(const roaring_array_t *source, roaring_array_t *dest, + bool copy_on_write) { + if (!ra_init_with_capacity(dest, source->size)) return false; + dest->size = source->size; + dest->allocation_size = source->size; + if(dest->size > 0) { + memcpy(dest->keys, source->keys, dest->size * sizeof(uint16_t)); + } + // we go through the containers, turning them into shared containers... + if (copy_on_write) { + for (int32_t i = 0; i < dest->size; ++i) { + source->containers[i] = get_copy_of_container( + source->containers[i], &source->typecodes[i], copy_on_write); + } + // we do a shallow copy to the other bitmap + if(dest->size > 0) { + memcpy(dest->containers, source->containers, + dest->size * sizeof(void *)); + memcpy(dest->typecodes, source->typecodes, + dest->size * sizeof(uint8_t)); + } + } else { + if(dest->size > 0) { + memcpy(dest->typecodes, source->typecodes, + dest->size * sizeof(uint8_t)); + } + for (int32_t i = 0; i < dest->size; i++) { + dest->containers[i] = + container_clone(source->containers[i], source->typecodes[i]); + if (dest->containers[i] == NULL) { + for (int32_t j = 0; j < i; j++) { + container_free(dest->containers[j], dest->typecodes[j]); + } + ra_clear_without_containers(dest); + return false; + } + } + } + return true; +} + +bool ra_overwrite(const roaring_array_t *source, roaring_array_t *dest, + bool copy_on_write) { + ra_clear_containers(dest); // we are going to overwrite them + if (dest->allocation_size < source->size) { + if (!realloc_array(dest, source->size)) { + return false; + } + } + dest->size = source->size; + memcpy(dest->keys, source->keys, dest->size * sizeof(uint16_t)); + // we go through the containers, turning them into shared containers... + if (copy_on_write) { + for (int32_t i = 0; i < dest->size; ++i) { + source->containers[i] = get_copy_of_container( + source->containers[i], &source->typecodes[i], copy_on_write); + } + // we do a shallow copy to the other bitmap + memcpy(dest->containers, source->containers, + dest->size * sizeof(void *)); + memcpy(dest->typecodes, source->typecodes, + dest->size * sizeof(uint8_t)); + } else { + memcpy(dest->typecodes, source->typecodes, + dest->size * sizeof(uint8_t)); + for (int32_t i = 0; i < dest->size; i++) { + dest->containers[i] = + container_clone(source->containers[i], source->typecodes[i]); + if (dest->containers[i] == NULL) { + for (int32_t j = 0; j < i; j++) { + container_free(dest->containers[j], dest->typecodes[j]); + } + ra_clear_without_containers(dest); + return false; + } + } + } + return true; +} + +void ra_clear_containers(roaring_array_t *ra) { + for (int32_t i = 0; i < ra->size; ++i) { + container_free(ra->containers[i], ra->typecodes[i]); + } +} + +void ra_reset(roaring_array_t *ra) { + ra_clear_containers(ra); + ra->size = 0; + ra_shrink_to_fit(ra); +} + +void ra_clear_without_containers(roaring_array_t *ra) { + free(ra->containers); // keys and typecodes are allocated with containers + ra->size = 0; + ra->allocation_size = 0; + ra->containers = NULL; + ra->keys = NULL; + ra->typecodes = NULL; +} + +void ra_clear(roaring_array_t *ra) { + ra_clear_containers(ra); + ra_clear_without_containers(ra); +} + +bool extend_array(roaring_array_t *ra, int32_t k) { + int32_t desired_size = ra->size + k; + assert(desired_size <= MAX_CONTAINERS); + if (desired_size > ra->allocation_size) { + int32_t new_capacity = + (ra->size < 1024) ? 2 * desired_size : 5 * desired_size / 4; + if (new_capacity > MAX_CONTAINERS) { + new_capacity = MAX_CONTAINERS; + } + + return realloc_array(ra, new_capacity); + } + return true; +} + +void ra_append(roaring_array_t *ra, uint16_t key, void *container, + uint8_t typecode) { + extend_array(ra, 1); + const int32_t pos = ra->size; + + ra->keys[pos] = key; + ra->containers[pos] = container; + ra->typecodes[pos] = typecode; + ra->size++; +} + +void ra_append_copy(roaring_array_t *ra, const roaring_array_t *sa, + uint16_t index, bool copy_on_write) { + extend_array(ra, 1); + const int32_t pos = ra->size; + + // old contents is junk not needing freeing + ra->keys[pos] = sa->keys[index]; + // the shared container will be in two bitmaps + if (copy_on_write) { + sa->containers[index] = get_copy_of_container( + sa->containers[index], &sa->typecodes[index], copy_on_write); + ra->containers[pos] = sa->containers[index]; + ra->typecodes[pos] = sa->typecodes[index]; + } else { + ra->containers[pos] = + container_clone(sa->containers[index], sa->typecodes[index]); + ra->typecodes[pos] = sa->typecodes[index]; + } + ra->size++; +} + +void ra_append_copies_until(roaring_array_t *ra, const roaring_array_t *sa, + uint16_t stopping_key, bool copy_on_write) { + for (int32_t i = 0; i < sa->size; ++i) { + if (sa->keys[i] >= stopping_key) break; + ra_append_copy(ra, sa, i, copy_on_write); + } +} + +void ra_append_copy_range(roaring_array_t *ra, const roaring_array_t *sa, + int32_t start_index, int32_t end_index, + bool copy_on_write) { + extend_array(ra, end_index - start_index); + for (int32_t i = start_index; i < end_index; ++i) { + const int32_t pos = ra->size; + ra->keys[pos] = sa->keys[i]; + if (copy_on_write) { + sa->containers[i] = get_copy_of_container( + sa->containers[i], &sa->typecodes[i], copy_on_write); + ra->containers[pos] = sa->containers[i]; + ra->typecodes[pos] = sa->typecodes[i]; + } else { + ra->containers[pos] = + container_clone(sa->containers[i], sa->typecodes[i]); + ra->typecodes[pos] = sa->typecodes[i]; + } + ra->size++; + } +} + +void ra_append_copies_after(roaring_array_t *ra, const roaring_array_t *sa, + uint16_t before_start, bool copy_on_write) { + int start_location = ra_get_index(sa, before_start); + if (start_location >= 0) + ++start_location; + else + start_location = -start_location - 1; + ra_append_copy_range(ra, sa, start_location, sa->size, copy_on_write); +} + +void ra_append_move_range(roaring_array_t *ra, roaring_array_t *sa, + int32_t start_index, int32_t end_index) { + extend_array(ra, end_index - start_index); + + for (int32_t i = start_index; i < end_index; ++i) { + const int32_t pos = ra->size; + + ra->keys[pos] = sa->keys[i]; + ra->containers[pos] = sa->containers[i]; + ra->typecodes[pos] = sa->typecodes[i]; + ra->size++; + } +} + +void ra_append_range(roaring_array_t *ra, roaring_array_t *sa, + int32_t start_index, int32_t end_index, + bool copy_on_write) { + extend_array(ra, end_index - start_index); + + for (int32_t i = start_index; i < end_index; ++i) { + const int32_t pos = ra->size; + ra->keys[pos] = sa->keys[i]; + if (copy_on_write) { + sa->containers[i] = get_copy_of_container( + sa->containers[i], &sa->typecodes[i], copy_on_write); + ra->containers[pos] = sa->containers[i]; + ra->typecodes[pos] = sa->typecodes[i]; + } else { + ra->containers[pos] = + container_clone(sa->containers[i], sa->typecodes[i]); + ra->typecodes[pos] = sa->typecodes[i]; + } + ra->size++; + } +} + +uint16_t ra_get_key_at_index(const roaring_array_t *ra, uint16_t i) { + return ra->keys[i]; +} + +// everything skipped over is freed +int32_t ra_advance_until_freeing(roaring_array_t *ra, uint16_t x, int32_t pos) { + while (pos < ra->size && ra->keys[pos] < x) { + container_free(ra->containers[pos], ra->typecodes[pos]); + ++pos; + } + return pos; +} + +void ra_insert_new_key_value_at(roaring_array_t *ra, int32_t i, uint16_t key, + void *container, uint8_t typecode) { + extend_array(ra, 1); + // May be an optimization opportunity with DIY memmove + memmove(&(ra->keys[i + 1]), &(ra->keys[i]), + sizeof(uint16_t) * (ra->size - i)); + memmove(&(ra->containers[i + 1]), &(ra->containers[i]), + sizeof(void *) * (ra->size - i)); + memmove(&(ra->typecodes[i + 1]), &(ra->typecodes[i]), + sizeof(uint8_t) * (ra->size - i)); + ra->keys[i] = key; + ra->containers[i] = container; + ra->typecodes[i] = typecode; + ra->size++; +} + +// note: Java routine set things to 0, enabling GC. +// Java called it "resize" but it was always used to downsize. +// Allowing upsize would break the conventions about +// valid containers below ra->size. + +void ra_downsize(roaring_array_t *ra, int32_t new_length) { + assert(new_length <= ra->size); + ra->size = new_length; +} + +void ra_remove_at_index(roaring_array_t *ra, int32_t i) { + memmove(&(ra->containers[i]), &(ra->containers[i + 1]), + sizeof(void *) * (ra->size - i - 1)); + memmove(&(ra->keys[i]), &(ra->keys[i + 1]), + sizeof(uint16_t) * (ra->size - i - 1)); + memmove(&(ra->typecodes[i]), &(ra->typecodes[i + 1]), + sizeof(uint8_t) * (ra->size - i - 1)); + ra->size--; +} + +void ra_remove_at_index_and_free(roaring_array_t *ra, int32_t i) { + container_free(ra->containers[i], ra->typecodes[i]); + ra_remove_at_index(ra, i); +} + +// used in inplace andNot only, to slide left the containers from +// the mutated RoaringBitmap that are after the largest container of +// the argument RoaringBitmap. In use it should be followed by a call to +// downsize. +// +void ra_copy_range(roaring_array_t *ra, uint32_t begin, uint32_t end, + uint32_t new_begin) { + assert(begin <= end); + assert(new_begin < begin); + + const int range = end - begin; + + // We ensure to previously have freed overwritten containers + // that are not copied elsewhere + + memmove(&(ra->containers[new_begin]), &(ra->containers[begin]), + sizeof(void *) * range); + memmove(&(ra->keys[new_begin]), &(ra->keys[begin]), + sizeof(uint16_t) * range); + memmove(&(ra->typecodes[new_begin]), &(ra->typecodes[begin]), + sizeof(uint8_t) * range); +} + +void ra_shift_tail(roaring_array_t *ra, int32_t count, int32_t distance) { + if (distance > 0) { + extend_array(ra, distance); + } + int32_t srcpos = ra->size - count; + int32_t dstpos = srcpos + distance; + memmove(&(ra->keys[dstpos]), &(ra->keys[srcpos]), + sizeof(uint16_t) * count); + memmove(&(ra->containers[dstpos]), &(ra->containers[srcpos]), + sizeof(void *) * count); + memmove(&(ra->typecodes[dstpos]), &(ra->typecodes[srcpos]), + sizeof(uint8_t) * count); + ra->size += distance; +} + + +void ra_to_uint32_array(const roaring_array_t *ra, uint32_t *ans) { + size_t ctr = 0; + for (int32_t i = 0; i < ra->size; ++i) { + int num_added = container_to_uint32_array( + ans + ctr, ra->containers[i], ra->typecodes[i], + ((uint32_t)ra->keys[i]) << 16); + ctr += num_added; + } +} + +bool ra_range_uint32_array(const roaring_array_t *ra, size_t offset, size_t limit, uint32_t *ans) { + size_t ctr = 0; + size_t dtr = 0; + + size_t t_limit = 0; + + bool first = false; + size_t first_skip = 0; + + uint32_t *t_ans = NULL; + size_t cur_len = 0; + + for (int i = 0; i < ra->size; ++i) { + + const void *container = container_unwrap_shared(ra->containers[i], &ra->typecodes[i]); + switch (ra->typecodes[i]) { + case BITSET_CONTAINER_TYPE_CODE: + t_limit = ((const bitset_container_t *)container)->cardinality; + break; + case ARRAY_CONTAINER_TYPE_CODE: + t_limit = ((const array_container_t *)container)->cardinality; + break; + case RUN_CONTAINER_TYPE_CODE: + t_limit = run_container_cardinality((const run_container_t *)container); + break; + case SHARED_CONTAINER_TYPE_CODE: + default: + __builtin_unreachable(); + } + if (ctr + t_limit - 1 >= offset && ctr < offset + limit){ + if (!first){ + //first_skip = t_limit - (ctr + t_limit - offset); + first_skip = offset - ctr; + first = true; + t_ans = (uint32_t *)malloc(sizeof(*t_ans) * (first_skip + limit)); + if(t_ans == NULL) { + return false; + } + memset(t_ans, 0, sizeof(*t_ans) * (first_skip + limit)) ; + cur_len = first_skip + limit; + } + if (dtr + t_limit > cur_len){ + uint32_t * append_ans = (uint32_t *)malloc(sizeof(*append_ans) * (cur_len + t_limit)); + if(append_ans == NULL) { + if(t_ans != NULL) free(t_ans); + return false; + } + memset(append_ans, 0, sizeof(*append_ans) * (cur_len + t_limit)); + cur_len = cur_len + t_limit; + memcpy(append_ans, t_ans, dtr * sizeof(uint32_t)); + free(t_ans); + t_ans = append_ans; + } + switch (ra->typecodes[i]) { + case BITSET_CONTAINER_TYPE_CODE: + container_to_uint32_array( + t_ans + dtr, (const bitset_container_t *)container, ra->typecodes[i], + ((uint32_t)ra->keys[i]) << 16); + break; + case ARRAY_CONTAINER_TYPE_CODE: + container_to_uint32_array( + t_ans + dtr, (const array_container_t *)container, ra->typecodes[i], + ((uint32_t)ra->keys[i]) << 16); + break; + case RUN_CONTAINER_TYPE_CODE: + container_to_uint32_array( + t_ans + dtr, (const run_container_t *)container, ra->typecodes[i], + ((uint32_t)ra->keys[i]) << 16); + break; + case SHARED_CONTAINER_TYPE_CODE: + default: + __builtin_unreachable(); + } + dtr += t_limit; + } + ctr += t_limit; + if (dtr-first_skip >= limit) break; + } + if(t_ans != NULL) { + memcpy(ans, t_ans+first_skip, limit * sizeof(uint32_t)); + free(t_ans); + } + return true; +} + +bool ra_has_run_container(const roaring_array_t *ra) { + for (int32_t k = 0; k < ra->size; ++k) { + if (get_container_type(ra->containers[k], ra->typecodes[k]) == + RUN_CONTAINER_TYPE_CODE) + return true; + } + return false; +} + +uint32_t ra_portable_header_size(const roaring_array_t *ra) { + if (ra_has_run_container(ra)) { + if (ra->size < + NO_OFFSET_THRESHOLD) { // for small bitmaps, we omit the offsets + return 4 + (ra->size + 7) / 8 + 4 * ra->size; + } + return 4 + (ra->size + 7) / 8 + + 8 * ra->size; // - 4 because we pack the size with the cookie + } else { + return 4 + 4 + 8 * ra->size; + } +} + +size_t ra_portable_size_in_bytes(const roaring_array_t *ra) { + size_t count = ra_portable_header_size(ra); + + for (int32_t k = 0; k < ra->size; ++k) { + count += container_size_in_bytes(ra->containers[k], ra->typecodes[k]); + } + return count; +} + +size_t ra_portable_serialize(const roaring_array_t *ra, char *buf) { + char *initbuf = buf; + uint32_t startOffset = 0; + bool hasrun = ra_has_run_container(ra); + if (hasrun) { + uint32_t cookie = SERIAL_COOKIE | ((ra->size - 1) << 16); + memcpy(buf, &cookie, sizeof(cookie)); + buf += sizeof(cookie); + uint32_t s = (ra->size + 7) / 8; + uint8_t *bitmapOfRunContainers = (uint8_t *)calloc(s, 1); + assert(bitmapOfRunContainers != NULL); // todo: handle + for (int32_t i = 0; i < ra->size; ++i) { + if (get_container_type(ra->containers[i], ra->typecodes[i]) == + RUN_CONTAINER_TYPE_CODE) { + bitmapOfRunContainers[i / 8] |= (1 << (i % 8)); + } + } + memcpy(buf, bitmapOfRunContainers, s); + buf += s; + free(bitmapOfRunContainers); + if (ra->size < NO_OFFSET_THRESHOLD) { + startOffset = 4 + 4 * ra->size + s; + } else { + startOffset = 4 + 8 * ra->size + s; + } + } else { // backwards compatibility + uint32_t cookie = SERIAL_COOKIE_NO_RUNCONTAINER; + + memcpy(buf, &cookie, sizeof(cookie)); + buf += sizeof(cookie); + memcpy(buf, &ra->size, sizeof(ra->size)); + buf += sizeof(ra->size); + + startOffset = 4 + 4 + 4 * ra->size + 4 * ra->size; + } + for (int32_t k = 0; k < ra->size; ++k) { + memcpy(buf, &ra->keys[k], sizeof(ra->keys[k])); + buf += sizeof(ra->keys[k]); + // get_cardinality returns a value in [1,1<<16], subtracting one + // we get [0,1<<16 - 1] which fits in 16 bits + uint16_t card = (uint16_t)( + container_get_cardinality(ra->containers[k], ra->typecodes[k]) - 1); + memcpy(buf, &card, sizeof(card)); + buf += sizeof(card); + } + if ((!hasrun) || (ra->size >= NO_OFFSET_THRESHOLD)) { + // writing the containers offsets + for (int32_t k = 0; k < ra->size; k++) { + memcpy(buf, &startOffset, sizeof(startOffset)); + buf += sizeof(startOffset); + startOffset = + startOffset + + container_size_in_bytes(ra->containers[k], ra->typecodes[k]); + } + } + for (int32_t k = 0; k < ra->size; ++k) { + buf += container_write(ra->containers[k], ra->typecodes[k], buf); + } + return buf - initbuf; +} + +// Quickly checks whether there is a serialized bitmap at the pointer, +// not exceeding size "maxbytes" in bytes. This function does not allocate +// memory dynamically. +// +// This function returns 0 if and only if no valid bitmap is found. +// Otherwise, it returns how many bytes are occupied. +// +size_t ra_portable_deserialize_size(const char *buf, const size_t maxbytes) { + size_t bytestotal = sizeof(int32_t);// for cookie + if(bytestotal > maxbytes) return 0; + uint32_t cookie; + memcpy(&cookie, buf, sizeof(int32_t)); + buf += sizeof(uint32_t); + if ((cookie & 0xFFFF) != SERIAL_COOKIE && + cookie != SERIAL_COOKIE_NO_RUNCONTAINER) { + return 0; + } + int32_t size; + + if ((cookie & 0xFFFF) == SERIAL_COOKIE) + size = (cookie >> 16) + 1; + else { + bytestotal += sizeof(int32_t); + if(bytestotal > maxbytes) return 0; + memcpy(&size, buf, sizeof(int32_t)); + buf += sizeof(uint32_t); + } + if (size > (1<<16)) { + return 0; // logically impossible + } + char *bitmapOfRunContainers = NULL; + bool hasrun = (cookie & 0xFFFF) == SERIAL_COOKIE; + if (hasrun) { + int32_t s = (size + 7) / 8; + bytestotal += s; + if(bytestotal > maxbytes) return 0; + bitmapOfRunContainers = (char *)buf; + buf += s; + } + bytestotal += size * 2 * sizeof(uint16_t); + if(bytestotal > maxbytes) return 0; + uint16_t *keyscards = (uint16_t *)buf; + buf += size * 2 * sizeof(uint16_t); + if ((!hasrun) || (size >= NO_OFFSET_THRESHOLD)) { + // skipping the offsets + bytestotal += size * 4; + if(bytestotal > maxbytes) return 0; + buf += size * 4; + } + // Reading the containers + for (int32_t k = 0; k < size; ++k) { + uint16_t tmp; + memcpy(&tmp, keyscards + 2*k+1, sizeof(tmp)); + uint32_t thiscard = tmp + 1; + bool isbitmap = (thiscard > DEFAULT_MAX_SIZE); + bool isrun = false; + if(hasrun) { + if((bitmapOfRunContainers[k / 8] & (1 << (k % 8))) != 0) { + isbitmap = false; + isrun = true; + } + } + if (isbitmap) { + size_t containersize = BITSET_CONTAINER_SIZE_IN_WORDS * sizeof(uint64_t); + bytestotal += containersize; + if(bytestotal > maxbytes) return 0; + buf += containersize; + } else if (isrun) { + bytestotal += sizeof(uint16_t); + if(bytestotal > maxbytes) return 0; + uint16_t n_runs; + memcpy(&n_runs, buf, sizeof(uint16_t)); + buf += sizeof(uint16_t); + size_t containersize = n_runs * sizeof(rle16_t); + bytestotal += containersize; + if(bytestotal > maxbytes) return 0; + buf += containersize; + } else { + size_t containersize = thiscard * sizeof(uint16_t); + bytestotal += containersize; + if(bytestotal > maxbytes) return 0; + buf += containersize; + } + } + return bytestotal; +} + + +// this function populates answer from the content of buf (reading up to maxbytes bytes). +// The function returns false if a properly serialized bitmap cannot be found. +// if it returns true, readbytes is populated by how many bytes were read, we have that *readbytes <= maxbytes. +bool ra_portable_deserialize(roaring_array_t *answer, const char *buf, const size_t maxbytes, size_t * readbytes) { + *readbytes = sizeof(int32_t);// for cookie + if(*readbytes > maxbytes) { + fprintf(stderr, "Ran out of bytes while reading first 4 bytes.\n"); + return false; + } + uint32_t cookie; + memcpy(&cookie, buf, sizeof(int32_t)); + buf += sizeof(uint32_t); + if ((cookie & 0xFFFF) != SERIAL_COOKIE && + cookie != SERIAL_COOKIE_NO_RUNCONTAINER) { + fprintf(stderr, "I failed to find one of the right cookies. Found %" PRIu32 "\n", + cookie); + return false; + } + int32_t size; + + if ((cookie & 0xFFFF) == SERIAL_COOKIE) + size = (cookie >> 16) + 1; + else { + *readbytes += sizeof(int32_t); + if(*readbytes > maxbytes) { + fprintf(stderr, "Ran out of bytes while reading second part of the cookie.\n"); + return false; + } + memcpy(&size, buf, sizeof(int32_t)); + buf += sizeof(uint32_t); + } + if (size > (1<<16)) { + fprintf(stderr, "You cannot have so many containers, the data must be corrupted: %" PRId32 "\n", + size); + return false; // logically impossible + } + const char *bitmapOfRunContainers = NULL; + bool hasrun = (cookie & 0xFFFF) == SERIAL_COOKIE; + if (hasrun) { + int32_t s = (size + 7) / 8; + *readbytes += s; + if(*readbytes > maxbytes) {// data is corrupted? + fprintf(stderr, "Ran out of bytes while reading run bitmap.\n"); + return false; + } + bitmapOfRunContainers = buf; + buf += s; + } + uint16_t *keyscards = (uint16_t *)buf; + + *readbytes += size * 2 * sizeof(uint16_t); + if(*readbytes > maxbytes) { + fprintf(stderr, "Ran out of bytes while reading key-cardinality array.\n"); + return false; + } + buf += size * 2 * sizeof(uint16_t); + + bool is_ok = ra_init_with_capacity(answer, size); + if (!is_ok) { + fprintf(stderr, "Failed to allocate memory for roaring array. Bailing out.\n"); + return false; + } + + for (int32_t k = 0; k < size; ++k) { + uint16_t tmp; + memcpy(&tmp, keyscards + 2*k, sizeof(tmp)); + answer->keys[k] = tmp; + } + if ((!hasrun) || (size >= NO_OFFSET_THRESHOLD)) { + *readbytes += size * 4; + if(*readbytes > maxbytes) {// data is corrupted? + fprintf(stderr, "Ran out of bytes while reading offsets.\n"); + ra_clear(answer);// we need to clear the containers already allocated, and the roaring array + return false; + } + + // skipping the offsets + buf += size * 4; + } + // Reading the containers + for (int32_t k = 0; k < size; ++k) { + uint16_t tmp; + memcpy(&tmp, keyscards + 2*k+1, sizeof(tmp)); + uint32_t thiscard = tmp + 1; + bool isbitmap = (thiscard > DEFAULT_MAX_SIZE); + bool isrun = false; + if(hasrun) { + if((bitmapOfRunContainers[k / 8] & (1 << (k % 8))) != 0) { + isbitmap = false; + isrun = true; + } + } + if (isbitmap) { + // we check that the read is allowed + size_t containersize = BITSET_CONTAINER_SIZE_IN_WORDS * sizeof(uint64_t); + *readbytes += containersize; + if(*readbytes > maxbytes) { + fprintf(stderr, "Running out of bytes while reading a bitset container.\n"); + ra_clear(answer);// we need to clear the containers already allocated, and the roaring array + return false; + } + // it is now safe to read + bitset_container_t *c = bitset_container_create(); + if(c == NULL) {// memory allocation failure + fprintf(stderr, "Failed to allocate memory for a bitset container.\n"); + ra_clear(answer);// we need to clear the containers already allocated, and the roaring array + return false; + } + answer->size++; + buf += bitset_container_read(thiscard, c, buf); + answer->containers[k] = c; + answer->typecodes[k] = BITSET_CONTAINER_TYPE_CODE; + } else if (isrun) { + // we check that the read is allowed + *readbytes += sizeof(uint16_t); + if(*readbytes > maxbytes) { + fprintf(stderr, "Running out of bytes while reading a run container (header).\n"); + ra_clear(answer);// we need to clear the containers already allocated, and the roaring array + return false; + } + uint16_t n_runs; + memcpy(&n_runs, buf, sizeof(uint16_t)); + size_t containersize = n_runs * sizeof(rle16_t); + *readbytes += containersize; + if(*readbytes > maxbytes) {// data is corrupted? + fprintf(stderr, "Running out of bytes while reading a run container.\n"); + ra_clear(answer);// we need to clear the containers already allocated, and the roaring array + return false; + } + // it is now safe to read + + run_container_t *c = run_container_create(); + if(c == NULL) {// memory allocation failure + fprintf(stderr, "Failed to allocate memory for a run container.\n"); + ra_clear(answer);// we need to clear the containers already allocated, and the roaring array + return false; + } + answer->size++; + buf += run_container_read(thiscard, c, buf); + answer->containers[k] = c; + answer->typecodes[k] = RUN_CONTAINER_TYPE_CODE; + } else { + // we check that the read is allowed + size_t containersize = thiscard * sizeof(uint16_t); + *readbytes += containersize; + if(*readbytes > maxbytes) {// data is corrupted? + fprintf(stderr, "Running out of bytes while reading an array container.\n"); + ra_clear(answer);// we need to clear the containers already allocated, and the roaring array + return false; + } + // it is now safe to read + array_container_t *c = + array_container_create_given_capacity(thiscard); + if(c == NULL) {// memory allocation failure + fprintf(stderr, "Failed to allocate memory for an array container.\n"); + ra_clear(answer);// we need to clear the containers already allocated, and the roaring array + return false; + } + answer->size++; + buf += array_container_read(thiscard, c, buf); + answer->containers[k] = c; + answer->typecodes[k] = ARRAY_CONTAINER_TYPE_CODE; + } + } + return true; +} +/* end file src/roaring_array.c */ +/* begin file src/roaring_priority_queue.c */ + +struct roaring_pq_element_s { + uint64_t size; + bool is_temporary; + roaring_bitmap_t *bitmap; +}; + +typedef struct roaring_pq_element_s roaring_pq_element_t; + +struct roaring_pq_s { + roaring_pq_element_t *elements; + uint64_t size; +}; + +typedef struct roaring_pq_s roaring_pq_t; + +static inline bool compare(roaring_pq_element_t *t1, roaring_pq_element_t *t2) { + return t1->size < t2->size; +} + +static void pq_add(roaring_pq_t *pq, roaring_pq_element_t *t) { + uint64_t i = pq->size; + pq->elements[pq->size++] = *t; + while (i > 0) { + uint64_t p = (i - 1) >> 1; + roaring_pq_element_t ap = pq->elements[p]; + if (!compare(t, &ap)) break; + pq->elements[i] = ap; + i = p; + } + pq->elements[i] = *t; +} + +static void pq_free(roaring_pq_t *pq) { + free(pq->elements); + pq->elements = NULL; // paranoid + free(pq); +} + +static void percolate_down(roaring_pq_t *pq, uint32_t i) { + uint32_t size = (uint32_t)pq->size; + uint32_t hsize = size >> 1; + roaring_pq_element_t ai = pq->elements[i]; + while (i < hsize) { + uint32_t l = (i << 1) + 1; + uint32_t r = l + 1; + roaring_pq_element_t bestc = pq->elements[l]; + if (r < size) { + if (compare(pq->elements + r, &bestc)) { + l = r; + bestc = pq->elements[r]; + } + } + if (!compare(&bestc, &ai)) { + break; + } + pq->elements[i] = bestc; + i = l; + } + pq->elements[i] = ai; +} + +static roaring_pq_t *create_pq(const roaring_bitmap_t **arr, uint32_t length) { + roaring_pq_t *answer = (roaring_pq_t *)malloc(sizeof(roaring_pq_t)); + answer->elements = + (roaring_pq_element_t *)malloc(sizeof(roaring_pq_element_t) * length); + answer->size = length; + for (uint32_t i = 0; i < length; i++) { + answer->elements[i].bitmap = (roaring_bitmap_t *)arr[i]; + answer->elements[i].is_temporary = false; + answer->elements[i].size = + roaring_bitmap_portable_size_in_bytes(arr[i]); + } + for (int32_t i = (length >> 1); i >= 0; i--) { + percolate_down(answer, i); + } + return answer; +} + +static roaring_pq_element_t pq_poll(roaring_pq_t *pq) { + roaring_pq_element_t ans = *pq->elements; + if (pq->size > 1) { + pq->elements[0] = pq->elements[--pq->size]; + percolate_down(pq, 0); + } else + --pq->size; + // memmove(pq->elements,pq->elements+1,(pq->size-1)*sizeof(roaring_pq_element_t));--pq->size; + return ans; +} + +// this function consumes and frees the inputs +static roaring_bitmap_t *lazy_or_from_lazy_inputs(roaring_bitmap_t *x1, + roaring_bitmap_t *x2) { + uint8_t container_result_type = 0; + const int length1 = ra_get_size(&x1->high_low_container), + length2 = ra_get_size(&x2->high_low_container); + if (0 == length1) { + roaring_bitmap_free(x1); + return x2; + } + if (0 == length2) { + roaring_bitmap_free(x2); + return x1; + } + uint32_t neededcap = length1 > length2 ? length2 : length1; + roaring_bitmap_t *answer = roaring_bitmap_create_with_capacity(neededcap); + int pos1 = 0, pos2 = 0; + uint8_t container_type_1, container_type_2; + uint16_t s1 = ra_get_key_at_index(&x1->high_low_container, pos1); + uint16_t s2 = ra_get_key_at_index(&x2->high_low_container, pos2); + while (true) { + if (s1 == s2) { + // todo: unsharing can be inefficient as it may create a clone where + // none + // is needed, but it has the benefit of being easy to reason about. + ra_unshare_container_at_index(&x1->high_low_container, pos1); + void *c1 = ra_get_container_at_index(&x1->high_low_container, pos1, + &container_type_1); + assert(container_type_1 != SHARED_CONTAINER_TYPE_CODE); + ra_unshare_container_at_index(&x2->high_low_container, pos2); + void *c2 = ra_get_container_at_index(&x2->high_low_container, pos2, + &container_type_2); + assert(container_type_2 != SHARED_CONTAINER_TYPE_CODE); + void *c; + + if ((container_type_2 == BITSET_CONTAINER_TYPE_CODE) && + (container_type_1 != BITSET_CONTAINER_TYPE_CODE)) { + c = container_lazy_ior(c2, container_type_2, c1, + container_type_1, + &container_result_type); + container_free(c1, container_type_1); + if (c != c2) { + container_free(c2, container_type_2); + } + } else { + c = container_lazy_ior(c1, container_type_1, c2, + container_type_2, + &container_result_type); + container_free(c2, container_type_2); + if (c != c1) { + container_free(c1, container_type_1); + } + } + // since we assume that the initial containers are non-empty, the + // result here + // can only be non-empty + ra_append(&answer->high_low_container, s1, c, + container_result_type); + ++pos1; + ++pos2; + if (pos1 == length1) break; + if (pos2 == length2) break; + s1 = ra_get_key_at_index(&x1->high_low_container, pos1); + s2 = ra_get_key_at_index(&x2->high_low_container, pos2); + + } else if (s1 < s2) { // s1 < s2 + void *c1 = ra_get_container_at_index(&x1->high_low_container, pos1, + &container_type_1); + ra_append(&answer->high_low_container, s1, c1, container_type_1); + pos1++; + if (pos1 == length1) break; + s1 = ra_get_key_at_index(&x1->high_low_container, pos1); + + } else { // s1 > s2 + void *c2 = ra_get_container_at_index(&x2->high_low_container, pos2, + &container_type_2); + ra_append(&answer->high_low_container, s2, c2, container_type_2); + pos2++; + if (pos2 == length2) break; + s2 = ra_get_key_at_index(&x2->high_low_container, pos2); + } + } + if (pos1 == length1) { + ra_append_move_range(&answer->high_low_container, + &x2->high_low_container, pos2, length2); + } else if (pos2 == length2) { + ra_append_move_range(&answer->high_low_container, + &x1->high_low_container, pos1, length1); + } + ra_clear_without_containers(&x1->high_low_container); + ra_clear_without_containers(&x2->high_low_container); + free(x1); + free(x2); + return answer; +} + +/** + * Compute the union of 'number' bitmaps using a heap. This can + * sometimes be faster than roaring_bitmap_or_many which uses + * a naive algorithm. Caller is responsible for freeing the + * result. + */ +roaring_bitmap_t *roaring_bitmap_or_many_heap(uint32_t number, + const roaring_bitmap_t **x) { + if (number == 0) { + return roaring_bitmap_create(); + } + if (number == 1) { + return roaring_bitmap_copy(x[0]); + } + roaring_pq_t *pq = create_pq(x, number); + while (pq->size > 1) { + roaring_pq_element_t x1 = pq_poll(pq); + roaring_pq_element_t x2 = pq_poll(pq); + + if (x1.is_temporary && x2.is_temporary) { + roaring_bitmap_t *newb = + lazy_or_from_lazy_inputs(x1.bitmap, x2.bitmap); + // should normally return a fresh new bitmap *except* that + // it can return x1.bitmap or x2.bitmap in degenerate cases + bool temporary = !((newb == x1.bitmap) && (newb == x2.bitmap)); + uint64_t bsize = roaring_bitmap_portable_size_in_bytes(newb); + roaring_pq_element_t newelement = { + .size = bsize, .is_temporary = temporary, .bitmap = newb}; + pq_add(pq, &newelement); + } else if (x2.is_temporary) { + roaring_bitmap_lazy_or_inplace(x2.bitmap, x1.bitmap, false); + x2.size = roaring_bitmap_portable_size_in_bytes(x2.bitmap); + pq_add(pq, &x2); + } else if (x1.is_temporary) { + roaring_bitmap_lazy_or_inplace(x1.bitmap, x2.bitmap, false); + x1.size = roaring_bitmap_portable_size_in_bytes(x1.bitmap); + + pq_add(pq, &x1); + } else { + roaring_bitmap_t *newb = + roaring_bitmap_lazy_or(x1.bitmap, x2.bitmap, false); + uint64_t bsize = roaring_bitmap_portable_size_in_bytes(newb); + roaring_pq_element_t newelement = { + .size = bsize, .is_temporary = true, .bitmap = newb}; + + pq_add(pq, &newelement); + } + } + roaring_pq_element_t X = pq_poll(pq); + roaring_bitmap_t *answer = X.bitmap; + roaring_bitmap_repair_after_lazy(answer); + pq_free(pq); + return answer; +} +/* end file src/roaring_priority_queue.c */ + +#pragma GCC diagnostic pop diff --git a/contrib/eggbitset/roaring.h b/contrib/eggbitset/roaring.h new file mode 100644 index 00000000..1b5a5937 --- /dev/null +++ b/contrib/eggbitset/roaring.h @@ -0,0 +1,7090 @@ +/* + * Amalgamated copy of CRoaring 0.2.66, modified for GTK to reduce compiler + * warnings. + * + * Copyright 2016-2020 The CRoaring authors + * Copyright 2020 Benjamin Otte + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* begin file include/roaring/roaring_version.h */ +// /include/roaring/roaring_version.h automatically generated by release.py, do not change by hand +#ifndef ROARING_INCLUDE_ROARING_VERSION +#define ROARING_INCLUDE_ROARING_VERSION +#define ROARING_VERSION = 0.2.66, +enum { + ROARING_VERSION_MAJOR = 0, + ROARING_VERSION_MINOR = 2, + ROARING_VERSION_REVISION = 66 +}; +#endif // ROARING_INCLUDE_ROARING_VERSION +/* end file include/roaring/roaring_version.h */ +/* begin file include/roaring/portability.h */ +/* + * portability.h + * + */ + +#ifndef INCLUDE_PORTABILITY_H_ +#define INCLUDE_PORTABILITY_H_ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#ifndef __STDC_FORMAT_MACROS +#define __STDC_FORMAT_MACROS 1 +#endif + +#if !(defined(_POSIX_C_SOURCE)) || (_POSIX_C_SOURCE < 200809L) +#define _POSIX_C_SOURCE 200809L +#endif +#if !(defined(_XOPEN_SOURCE)) || (_XOPEN_SOURCE < 700) +#define _XOPEN_SOURCE 700 +#endif + +#include +#include +#include // will provide posix_memalign with _POSIX_C_SOURCE as defined above +#if !(defined(__APPLE__)) && !(defined(__FreeBSD__)) && !(defined(__OpenBSD__)) +#include // this should never be needed but there are some reports that it is needed. +#endif + + +#if defined(_MSC_VER) && !defined(__clang__) && !defined(_WIN64) && !defined(ROARING_ACK_32BIT) +#pragma message( \ + "You appear to be attempting a 32-bit build under Visual Studio. We recommend a 64-bit build instead.") +#endif + +#if defined(__SIZEOF_LONG_LONG__) && __SIZEOF_LONG_LONG__ != 8 +#error This code assumes 64-bit long longs (by use of the GCC intrinsics). Your system is not currently supported. +#endif + +#if defined(_MSC_VER) +#define __restrict__ __restrict +#endif + +#ifndef DISABLE_X64 // some users may want to compile as if they did not have + // an x64 processor + +/////////////////////// +/// We support X64 hardware in the following manner: +/// +/// if IS_X64 is defined then we have at least SSE and SSE2 +/// (All Intel processors sold in the recent past have at least SSE and SSE2 support, +/// going back to the Pentium 4.) +/// +/// if USESSE4 is defined then we assume at least SSE4.2, SSE4.1, +/// SSSE3, SSE3... + IS_X64 +/// if USEAVX is defined, then we assume AVX2, AVX + USESSE4 +/// +/// So if you have hardware that supports AVX but not AVX2, then "USEAVX" +/// won't be enabled. +/// If you have hardware that supports SSE4.1, but not SSE4.2, then USESSE4 +/// won't be defined. +////////////////////// + +// unless DISABLEAVX was defined, if we have __AVX2__, we enable AVX +#if (!defined(USEAVX)) && (!defined(DISABLEAVX)) && (defined(__AVX2__)) +#define USEAVX +#endif + +// if we have __SSE4_2__, we enable SSE4 +#if (defined(__POPCNT__)) && (defined(__SSE4_2__)) +#define USESSE4 +#endif + +#if defined(USEAVX) || defined(__x86_64__) || defined(_M_X64) +// we have an x64 processor +#define IS_X64 +// we include the intrinsic header +#ifndef _MSC_VER +/* Non-Microsoft C/C++-compatible compiler */ +#include // on some recent GCC, this will declare posix_memalign +#endif +#endif + +#if !defined(USENEON) && !defined(DISABLENEON) && defined(__ARM_NEON) +# define USENEON +#endif +#if defined(USENEON) +# include +#endif + +#ifndef _MSC_VER +/* Non-Microsoft C/C++-compatible compiler, assumes that it supports inline + * assembly */ +#define ROARING_INLINE_ASM +#endif + +#ifdef USEAVX +#define USESSE4 // if we have AVX, then we have SSE4 +#define USE_BMI // we assume that AVX2 and BMI go hand and hand +#define USEAVX2FORDECODING // optimization +// vector operations should work on not just AVX +#define ROARING_VECTOR_OPERATIONS_ENABLED // vector unions (optimization) +#endif + +#endif // DISABLE_X64 + +#ifdef _MSC_VER +/* Microsoft C/C++-compatible compiler */ +#include + +#ifndef __clang__ // if one compiles with MSVC *with* clang, then these + // intrinsics are defined!!! +// sadly there is no way to check whether we are missing these intrinsics +// specifically. + +/* wrappers for Visual Studio built-ins that look like gcc built-ins */ +/* result might be undefined when input_num is zero */ +static inline int __builtin_ctzll(unsigned long long input_num) { + unsigned long index; +#ifdef _WIN64 // highly recommended!!! + _BitScanForward64(&index, input_num); +#else // if we must support 32-bit Windows + if ((uint32_t)input_num != 0) { + _BitScanForward(&index, (uint32_t)input_num); + } else { + _BitScanForward(&index, (uint32_t)(input_num >> 32)); + index += 32; + } +#endif + return index; +} + +/* result might be undefined when input_num is zero */ +static inline int __builtin_clzll(unsigned long long input_num) { + unsigned long index; +#ifdef _WIN64 // highly recommended!!! + _BitScanReverse64(&index, input_num); +#else // if we must support 32-bit Windows + if (input_num > 0xFFFFFFFF) { + _BitScanReverse(&index, (uint32_t)(input_num >> 32)); + index += 32; + } else { + _BitScanReverse(&index, (uint32_t)(input_num)); + } +#endif + return 63 - index; +} + +/* result might be undefined when input_num is zero */ +#ifdef USESSE4 +/* POPCNT support was added to processors around the release of SSE4.2 */ +/* USESSE4 flag guarantees POPCNT support */ +static inline int __builtin_popcountll(unsigned long long input_num) { +#ifdef _WIN64 // highly recommended!!! + return (int)__popcnt64(input_num); +#else // if we must support 32-bit Windows + return (int)(__popcnt((uint32_t)input_num) + + __popcnt((uint32_t)(input_num >> 32))); +#endif +} +#else +/* software implementation avoids POPCNT */ +static inline int __builtin_popcountll(unsigned long long input_num) { + const uint64_t m1 = 0x5555555555555555; //binary: 0101... + const uint64_t m2 = 0x3333333333333333; //binary: 00110011.. + const uint64_t m4 = 0x0f0f0f0f0f0f0f0f; //binary: 4 zeros, 4 ones ... + const uint64_t h01 = 0x0101010101010101; //the sum of 256 to the power of 0,1,2,3... + + input_num -= (input_num >> 1) & m1; + input_num = (input_num & m2) + ((input_num >> 2) & m2); + input_num = (input_num + (input_num >> 4)) & m4; + return (input_num * h01) >> 56; +} +#endif + +/* Use #define so this is effective even under /Ob0 (no inline) */ +#define __builtin_unreachable() __assume(0) +#endif + +#endif + +// portable version of posix_memalign +static inline void *roaring_bitmap_aligned_malloc(size_t alignment, size_t size) { + void *p; +#ifdef _MSC_VER + p = _aligned_malloc(size, alignment); +#elif defined(__MINGW32__) || defined(__MINGW64__) + p = __mingw_aligned_malloc(size, alignment); +#else + // somehow, if this is used before including "x86intrin.h", it creates an + // implicit defined warning. + if (posix_memalign(&p, alignment, size) != 0) return NULL; +#endif + return p; +} + +static inline void roaring_bitmap_aligned_free(void *memblock) { +#ifdef _MSC_VER + _aligned_free(memblock); +#elif defined(__MINGW32__) || defined(__MINGW64__) + __mingw_aligned_free(memblock); +#else + free(memblock); +#endif +} + +#if defined(_MSC_VER) +#define ALIGNED(x) __declspec(align(x)) +#else +#if defined(__GNUC__) +#define ALIGNED(x) __attribute__((aligned(x))) +#endif +#endif + +#ifdef __GNUC__ +#define WARN_UNUSED __attribute__((warn_unused_result)) +#else +#define WARN_UNUSED +#endif + +#define IS_BIG_ENDIAN (*(uint16_t *)"\0\xff" < 0x100) + +static inline int hamming(uint64_t x) { +#ifdef USESSE4 + return (int) _mm_popcnt_u64(x); +#else + // won't work under visual studio, but hopeful we have _mm_popcnt_u64 in + // many cases + return __builtin_popcountll(x); +#endif +} + +#ifndef UINT64_C +#define UINT64_C(c) (c##ULL) +#endif + +#ifndef UINT32_C +#define UINT32_C(c) (c##UL) +#endif + +#endif /* INCLUDE_PORTABILITY_H_ */ +/* end file include/roaring/portability.h */ +/* begin file include/roaring/containers/perfparameters.h */ +#ifndef PERFPARAMETERS_H_ +#define PERFPARAMETERS_H_ + +#include + +/** +During lazy computations, we can transform array containers into bitset +containers as +long as we can expect them to have ARRAY_LAZY_LOWERBOUND values. +*/ +enum { ARRAY_LAZY_LOWERBOUND = 1024 }; + +/* default initial size of a run container + setting it to zero delays the malloc.*/ +enum { RUN_DEFAULT_INIT_SIZE = 0 }; + +/* default initial size of an array container + setting it to zero delays the malloc */ +enum { ARRAY_DEFAULT_INIT_SIZE = 0 }; + +/* automatic bitset conversion during lazy or */ +#ifndef LAZY_OR_BITSET_CONVERSION +#define LAZY_OR_BITSET_CONVERSION true +#endif + +/* automatically attempt to convert a bitset to a full run during lazy + * evaluation */ +#ifndef LAZY_OR_BITSET_CONVERSION_TO_FULL +#define LAZY_OR_BITSET_CONVERSION_TO_FULL true +#endif + +/* automatically attempt to convert a bitset to a full run */ +#ifndef OR_BITSET_CONVERSION_TO_FULL +#define OR_BITSET_CONVERSION_TO_FULL true +#endif + +#endif +/* end file include/roaring/containers/perfparameters.h */ +/* begin file include/roaring/array_util.h */ +#ifndef ARRAY_UTIL_H +#define ARRAY_UTIL_H + +#include // for size_t +#include + + +/* + * Good old binary search. + * Assumes that array is sorted, has logarithmic complexity. + * if the result is x, then: + * if ( x>0 ) you have array[x] = ikey + * if ( x<0 ) then inserting ikey at position -x-1 in array (insuring that array[-x-1]=ikey) + * keys the array sorted. + */ +static inline int32_t binarySearch(const uint16_t *array, int32_t lenarray, + uint16_t ikey) { + int32_t low = 0; + int32_t high = lenarray - 1; + while (low <= high) { + int32_t middleIndex = (low + high) >> 1; + uint16_t middleValue = array[middleIndex]; + if (middleValue < ikey) { + low = middleIndex + 1; + } else if (middleValue > ikey) { + high = middleIndex - 1; + } else { + return middleIndex; + } + } + return -(low + 1); +} + +/** + * Galloping search + * Assumes that array is sorted, has logarithmic complexity. + * if the result is x, then if x = length, you have that all values in array between pos and length + * are smaller than min. + * otherwise returns the first index x such that array[x] >= min. + */ +static inline int32_t advanceUntil(const uint16_t *array, int32_t pos, + int32_t length, uint16_t min) { + int32_t lower = pos + 1; + + if ((lower >= length) || (array[lower] >= min)) { + return lower; + } + + int32_t spansize = 1; + + while ((lower + spansize < length) && (array[lower + spansize] < min)) { + spansize <<= 1; + } + int32_t upper = (lower + spansize < length) ? lower + spansize : length - 1; + + if (array[upper] == min) { + return upper; + } + if (array[upper] < min) { + // means + // array + // has no + // item + // >= min + // pos = array.length; + return length; + } + + // we know that the next-smallest span was too small + lower += (spansize >> 1); + + int32_t mid = 0; + while (lower + 1 != upper) { + mid = (lower + upper) >> 1; + if (array[mid] == min) { + return mid; + } else if (array[mid] < min) { + lower = mid; + } else { + upper = mid; + } + } + return upper; +} + +/** + * Returns number of elements which are less then $ikey. + * Array elements must be unique and sorted. + */ +static inline int32_t count_less(const uint16_t *array, int32_t lenarray, + uint16_t ikey) { + if (lenarray == 0) return 0; + int32_t pos = binarySearch(array, lenarray, ikey); + return pos >= 0 ? pos : -(pos+1); +} + +/** + * Returns number of elements which are greater then $ikey. + * Array elements must be unique and sorted. + */ +static inline int32_t count_greater(const uint16_t *array, int32_t lenarray, + uint16_t ikey) { + if (lenarray == 0) return 0; + int32_t pos = binarySearch(array, lenarray, ikey); + if (pos >= 0) { + return lenarray - (pos+1); + } else { + return lenarray - (-pos-1); + } +} + +/** + * From Schlegel et al., Fast Sorted-Set Intersection using SIMD Instructions + * Optimized by D. Lemire on May 3rd 2013 + * + * C should have capacity greater than the minimum of s_1 and s_b + 8 + * where 8 is sizeof(__m128i)/sizeof(uint16_t). + */ +int32_t intersect_vector16(const uint16_t *__restrict__ A, size_t s_a, + const uint16_t *__restrict__ B, size_t s_b, + uint16_t *C); + +/** + * Compute the cardinality of the intersection using SSE4 instructions + */ +int32_t intersect_vector16_cardinality(const uint16_t *__restrict__ A, + size_t s_a, + const uint16_t *__restrict__ B, + size_t s_b); + +/* Computes the intersection between one small and one large set of uint16_t. + * Stores the result into buffer and return the number of elements. */ +int32_t intersect_skewed_uint16(const uint16_t *smallarray, size_t size_s, + const uint16_t *largearray, size_t size_l, + uint16_t *buffer); + +/* Computes the size of the intersection between one small and one large set of + * uint16_t. */ +int32_t intersect_skewed_uint16_cardinality(const uint16_t *smallarray, + size_t size_s, + const uint16_t *largearray, + size_t size_l); + + +/* Check whether the size of the intersection between one small and one large set of uint16_t is non-zero. */ +bool intersect_skewed_uint16_nonempty(const uint16_t *smallarray, size_t size_s, + const uint16_t *largearray, size_t size_l); +/** + * Generic intersection function. + */ +int32_t intersect_uint16(const uint16_t *A, const size_t lenA, + const uint16_t *B, const size_t lenB, uint16_t *out); +/** + * Compute the size of the intersection (generic). + */ +int32_t intersect_uint16_cardinality(const uint16_t *A, const size_t lenA, + const uint16_t *B, const size_t lenB); + +/** + * Checking whether the size of the intersection is non-zero. + */ +bool intersect_uint16_nonempty(const uint16_t *A, const size_t lenA, + const uint16_t *B, const size_t lenB); +/** + * Generic union function. + */ +size_t union_uint16(const uint16_t *set_1, size_t size_1, const uint16_t *set_2, + size_t size_2, uint16_t *buffer); + +/** + * Generic XOR function. + */ +int32_t xor_uint16(const uint16_t *array_1, int32_t card_1, + const uint16_t *array_2, int32_t card_2, uint16_t *out); + +/** + * Generic difference function (ANDNOT). + */ +int difference_uint16(const uint16_t *a1, int length1, const uint16_t *a2, + int length2, uint16_t *a_out); + +/** + * Generic intersection function. + */ +size_t intersection_uint32(const uint32_t *A, const size_t lenA, + const uint32_t *B, const size_t lenB, uint32_t *out); + +/** + * Generic intersection function, returns just the cardinality. + */ +size_t intersection_uint32_card(const uint32_t *A, const size_t lenA, + const uint32_t *B, const size_t lenB); + +/** + * Generic union function. + */ +size_t union_uint32(const uint32_t *set_1, size_t size_1, const uint32_t *set_2, + size_t size_2, uint32_t *buffer); + +/** + * A fast SSE-based union function. + */ +uint32_t union_vector16(const uint16_t *__restrict__ set_1, uint32_t size_1, + const uint16_t *__restrict__ set_2, uint32_t size_2, + uint16_t *__restrict__ buffer); +/** + * A fast SSE-based XOR function. + */ +uint32_t xor_vector16(const uint16_t *__restrict__ array1, uint32_t length1, + const uint16_t *__restrict__ array2, uint32_t length2, + uint16_t *__restrict__ output); + +/** + * A fast SSE-based difference function. + */ +int32_t difference_vector16(const uint16_t *__restrict__ A, size_t s_a, + const uint16_t *__restrict__ B, size_t s_b, + uint16_t *C); + +/** + * Generic union function, returns just the cardinality. + */ +size_t union_uint32_card(const uint32_t *set_1, size_t size_1, + const uint32_t *set_2, size_t size_2); + +/** +* combines union_uint16 and union_vector16 optimally +*/ +size_t fast_union_uint16(const uint16_t *set_1, size_t size_1, const uint16_t *set_2, + size_t size_2, uint16_t *buffer); + + +bool memequals(const void *s1, const void *s2, size_t n); + +#endif +/* end file include/roaring/array_util.h */ +/* begin file include/roaring/roaring_types.h */ +/* + Typedefs used by various components +*/ + +#ifndef ROARING_TYPES_H +#define ROARING_TYPES_H + +typedef bool (*roaring_iterator)(uint32_t value, void *param); +typedef bool (*roaring_iterator64)(uint64_t value, void *param); + +/** +* (For advanced users.) +* The roaring_statistics_t can be used to collect detailed statistics about +* the composition of a roaring bitmap. +*/ +typedef struct roaring_statistics_s { + uint32_t n_containers; /* number of containers */ + + uint32_t n_array_containers; /* number of array containers */ + uint32_t n_run_containers; /* number of run containers */ + uint32_t n_bitset_containers; /* number of bitmap containers */ + + uint32_t + n_values_array_containers; /* number of values in array containers */ + uint32_t n_values_run_containers; /* number of values in run containers */ + uint32_t + n_values_bitset_containers; /* number of values in bitmap containers */ + + uint32_t n_bytes_array_containers; /* number of allocated bytes in array + containers */ + uint32_t n_bytes_run_containers; /* number of allocated bytes in run + containers */ + uint32_t n_bytes_bitset_containers; /* number of allocated bytes in bitmap + containers */ + + uint32_t + max_value; /* the maximal value, undefined if cardinality is zero */ + uint32_t + min_value; /* the minimal value, undefined if cardinality is zero */ + uint64_t sum_value; /* the sum of all values (could be used to compute + average) */ + + uint64_t cardinality; /* total number of values stored in the bitmap */ + + // and n_values_arrays, n_values_rle, n_values_bitmap +} roaring_statistics_t; + +#endif /* ROARING_TYPES_H */ +/* end file include/roaring/roaring_types.h */ +/* begin file include/roaring/utilasm.h */ +/* + * utilasm.h + * + */ + +#ifndef INCLUDE_UTILASM_H_ +#define INCLUDE_UTILASM_H_ + + +#if defined(USE_BMI) & defined(ROARING_INLINE_ASM) +#define ASMBITMANIPOPTIMIZATION // optimization flag + +#define ASM_SHIFT_RIGHT(srcReg, bitsReg, destReg) \ + __asm volatile("shrx %1, %2, %0" \ + : "=r"(destReg) \ + : /* write */ \ + "r"(bitsReg), /* read only */ \ + "r"(srcReg) /* read only */ \ + ) + +#define ASM_INPLACESHIFT_RIGHT(srcReg, bitsReg) \ + __asm volatile("shrx %1, %0, %0" \ + : "+r"(srcReg) \ + : /* read/write */ \ + "r"(bitsReg) /* read only */ \ + ) + +#define ASM_SHIFT_LEFT(srcReg, bitsReg, destReg) \ + __asm volatile("shlx %1, %2, %0" \ + : "=r"(destReg) \ + : /* write */ \ + "r"(bitsReg), /* read only */ \ + "r"(srcReg) /* read only */ \ + ) +// set bit at position testBit within testByte to 1 and +// copy cmovDst to cmovSrc if that bit was previously clear +#define ASM_SET_BIT_INC_WAS_CLEAR(testByte, testBit, count) \ + __asm volatile( \ + "bts %2, %0\n" \ + "sbb $-1, %1\n" \ + : "+r"(testByte), /* read/write */ \ + "+r"(count) \ + : /* read/write */ \ + "r"(testBit) /* read only */ \ + ) + +#define ASM_CLEAR_BIT_DEC_WAS_SET(testByte, testBit, count) \ + __asm volatile( \ + "btr %2, %0\n" \ + "sbb $0, %1\n" \ + : "+r"(testByte), /* read/write */ \ + "+r"(count) \ + : /* read/write */ \ + "r"(testBit) /* read only */ \ + ) + +#define ASM_BT64(testByte, testBit, count) \ + __asm volatile( \ + "bt %2,%1\n" \ + "sbb %0,%0" /*could use setb */ \ + : "=r"(count) \ + : /* write */ \ + "r"(testByte), /* read only */ \ + "r"(testBit) /* read only */ \ + ) + +#endif // USE_BMI +#endif /* INCLUDE_UTILASM_H_ */ +/* end file include/roaring/utilasm.h */ +/* begin file include/roaring/bitset_util.h */ +#ifndef BITSET_UTIL_H +#define BITSET_UTIL_H + +#include + + +/* + * Set all bits in indexes [begin,end) to true. + */ +static inline void bitset_set_range(uint64_t *bitmap, uint32_t start, + uint32_t end) { + if (start == end) return; + uint32_t firstword = start / 64; + uint32_t endword = (end - 1) / 64; + if (firstword == endword) { + bitmap[firstword] |= ((~UINT64_C(0)) << (start % 64)) & + ((~UINT64_C(0)) >> ((~end + 1) % 64)); + return; + } + bitmap[firstword] |= (~UINT64_C(0)) << (start % 64); + for (uint32_t i = firstword + 1; i < endword; i++) bitmap[i] = ~UINT64_C(0); + bitmap[endword] |= (~UINT64_C(0)) >> ((~end + 1) % 64); +} + + +/* + * Find the cardinality of the bitset in [begin,begin+lenminusone] + */ +static inline int bitset_lenrange_cardinality(uint64_t *bitmap, uint32_t start, + uint32_t lenminusone) { + uint32_t firstword = start / 64; + uint32_t endword = (start + lenminusone) / 64; + if (firstword == endword) { + return hamming(bitmap[firstword] & + ((~UINT64_C(0)) >> ((63 - lenminusone) % 64)) + << (start % 64)); + } + int answer = hamming(bitmap[firstword] & ((~UINT64_C(0)) << (start % 64))); + for (uint32_t i = firstword + 1; i < endword; i++) { + answer += hamming(bitmap[i]); + } + answer += + hamming(bitmap[endword] & + (~UINT64_C(0)) >> (((~start + 1) - lenminusone - 1) % 64)); + return answer; +} + +/* + * Check whether the cardinality of the bitset in [begin,begin+lenminusone] is 0 + */ +static inline bool bitset_lenrange_empty(uint64_t *bitmap, uint32_t start, + uint32_t lenminusone) { + uint32_t firstword = start / 64; + uint32_t endword = (start + lenminusone) / 64; + if (firstword == endword) { + return (bitmap[firstword] & ((~UINT64_C(0)) >> ((63 - lenminusone) % 64)) + << (start % 64)) == 0; + } + if(((bitmap[firstword] & ((~UINT64_C(0)) << (start%64)))) != 0) return false; + for (uint32_t i = firstword + 1; i < endword; i++) { + if(bitmap[i] != 0) return false; + } + if((bitmap[endword] & (~UINT64_C(0)) >> (((~start + 1) - lenminusone - 1) % 64)) != 0) return false; + return true; +} + + +/* + * Set all bits in indexes [begin,begin+lenminusone] to true. + */ +static inline void bitset_set_lenrange(uint64_t *bitmap, uint32_t start, + uint32_t lenminusone) { + uint32_t firstword = start / 64; + uint32_t endword = (start + lenminusone) / 64; + if (firstword == endword) { + bitmap[firstword] |= ((~UINT64_C(0)) >> ((63 - lenminusone) % 64)) + << (start % 64); + return; + } + uint64_t temp = bitmap[endword]; + bitmap[firstword] |= (~UINT64_C(0)) << (start % 64); + for (uint32_t i = firstword + 1; i < endword; i += 2) + bitmap[i] = bitmap[i + 1] = ~UINT64_C(0); + bitmap[endword] = + temp | (~UINT64_C(0)) >> (((~start + 1) - lenminusone - 1) % 64); +} + +/* + * Flip all the bits in indexes [begin,end). + */ +static inline void bitset_flip_range(uint64_t *bitmap, uint32_t start, + uint32_t end) { + if (start == end) return; + uint32_t firstword = start / 64; + uint32_t endword = (end - 1) / 64; + bitmap[firstword] ^= ~((~UINT64_C(0)) << (start % 64)); + for (uint32_t i = firstword; i < endword; i++) bitmap[i] = ~bitmap[i]; + bitmap[endword] ^= ((~UINT64_C(0)) >> ((~end + 1) % 64)); +} + +/* + * Set all bits in indexes [begin,end) to false. + */ +static inline void bitset_reset_range(uint64_t *bitmap, uint32_t start, + uint32_t end) { + if (start == end) return; + uint32_t firstword = start / 64; + uint32_t endword = (end - 1) / 64; + if (firstword == endword) { + bitmap[firstword] &= ~(((~UINT64_C(0)) << (start % 64)) & + ((~UINT64_C(0)) >> ((~end + 1) % 64))); + return; + } + bitmap[firstword] &= ~((~UINT64_C(0)) << (start % 64)); + for (uint32_t i = firstword + 1; i < endword; i++) bitmap[i] = UINT64_C(0); + bitmap[endword] &= ~((~UINT64_C(0)) >> ((~end + 1) % 64)); +} + +/* + * Given a bitset containing "length" 64-bit words, write out the position + * of all the set bits to "out", values start at "base". + * + * The "out" pointer should be sufficient to store the actual number of bits + * set. + * + * Returns how many values were actually decoded. + * + * This function should only be expected to be faster than + * bitset_extract_setbits + * when the density of the bitset is high. + * + * This function uses AVX2 decoding. + */ +size_t bitset_extract_setbits_avx2(uint64_t *bitset, size_t length, void *vout, + size_t outcapacity, uint32_t base); + +/* + * Given a bitset containing "length" 64-bit words, write out the position + * of all the set bits to "out", values start at "base". + * + * The "out" pointer should be sufficient to store the actual number of bits + *set. + * + * Returns how many values were actually decoded. + */ +size_t bitset_extract_setbits(uint64_t *bitset, size_t length, void *vout, + uint32_t base); + +/* + * Given a bitset containing "length" 64-bit words, write out the position + * of all the set bits to "out" as 16-bit integers, values start at "base" (can + *be set to zero) + * + * The "out" pointer should be sufficient to store the actual number of bits + *set. + * + * Returns how many values were actually decoded. + * + * This function should only be expected to be faster than + *bitset_extract_setbits_uint16 + * when the density of the bitset is high. + * + * This function uses SSE decoding. + */ +size_t bitset_extract_setbits_sse_uint16(const uint64_t *bitset, size_t length, + uint16_t *out, size_t outcapacity, + uint16_t base); + +/* + * Given a bitset containing "length" 64-bit words, write out the position + * of all the set bits to "out", values start at "base" + * (can be set to zero) + * + * The "out" pointer should be sufficient to store the actual number of bits + *set. + * + * Returns how many values were actually decoded. + */ +size_t bitset_extract_setbits_uint16(const uint64_t *bitset, size_t length, + uint16_t *out, uint16_t base); + +/* + * Given two bitsets containing "length" 64-bit words, write out the position + * of all the common set bits to "out", values start at "base" + * (can be set to zero) + * + * The "out" pointer should be sufficient to store the actual number of bits + * set. + * + * Returns how many values were actually decoded. + */ +size_t bitset_extract_intersection_setbits_uint16(const uint64_t * __restrict__ bitset1, + const uint64_t * __restrict__ bitset2, + size_t length, uint16_t *out, + uint16_t base); + +/* + * Given a bitset having cardinality card, set all bit values in the list (there + * are length of them) + * and return the updated cardinality. This evidently assumes that the bitset + * already contained data. + */ +uint64_t bitset_set_list_withcard(void *bitset, uint64_t card, + const uint16_t *list, uint64_t length); +/* + * Given a bitset, set all bit values in the list (there + * are length of them). + */ +void bitset_set_list(void *bitset, const uint16_t *list, uint64_t length); + +/* + * Given a bitset having cardinality card, unset all bit values in the list + * (there are length of them) + * and return the updated cardinality. This evidently assumes that the bitset + * already contained data. + */ +uint64_t bitset_clear_list(void *bitset, uint64_t card, const uint16_t *list, + uint64_t length); + +/* + * Given a bitset having cardinality card, toggle all bit values in the list + * (there are length of them) + * and return the updated cardinality. This evidently assumes that the bitset + * already contained data. + */ + +uint64_t bitset_flip_list_withcard(void *bitset, uint64_t card, + const uint16_t *list, uint64_t length); + +void bitset_flip_list(void *bitset, const uint16_t *list, uint64_t length); + +#ifdef USEAVX +/*** + * BEGIN Harley-Seal popcount functions. + */ + +/** + * Compute the population count of a 256-bit word + * This is not especially fast, but it is convenient as part of other functions. + */ +static inline __m256i popcount256(__m256i v) { + const __m256i lookuppos = _mm256_setr_epi8( + /* 0 */ 4 + 0, /* 1 */ 4 + 1, /* 2 */ 4 + 1, /* 3 */ 4 + 2, + /* 4 */ 4 + 1, /* 5 */ 4 + 2, /* 6 */ 4 + 2, /* 7 */ 4 + 3, + /* 8 */ 4 + 1, /* 9 */ 4 + 2, /* a */ 4 + 2, /* b */ 4 + 3, + /* c */ 4 + 2, /* d */ 4 + 3, /* e */ 4 + 3, /* f */ 4 + 4, + + /* 0 */ 4 + 0, /* 1 */ 4 + 1, /* 2 */ 4 + 1, /* 3 */ 4 + 2, + /* 4 */ 4 + 1, /* 5 */ 4 + 2, /* 6 */ 4 + 2, /* 7 */ 4 + 3, + /* 8 */ 4 + 1, /* 9 */ 4 + 2, /* a */ 4 + 2, /* b */ 4 + 3, + /* c */ 4 + 2, /* d */ 4 + 3, /* e */ 4 + 3, /* f */ 4 + 4); + const __m256i lookupneg = _mm256_setr_epi8( + /* 0 */ 4 - 0, /* 1 */ 4 - 1, /* 2 */ 4 - 1, /* 3 */ 4 - 2, + /* 4 */ 4 - 1, /* 5 */ 4 - 2, /* 6 */ 4 - 2, /* 7 */ 4 - 3, + /* 8 */ 4 - 1, /* 9 */ 4 - 2, /* a */ 4 - 2, /* b */ 4 - 3, + /* c */ 4 - 2, /* d */ 4 - 3, /* e */ 4 - 3, /* f */ 4 - 4, + + /* 0 */ 4 - 0, /* 1 */ 4 - 1, /* 2 */ 4 - 1, /* 3 */ 4 - 2, + /* 4 */ 4 - 1, /* 5 */ 4 - 2, /* 6 */ 4 - 2, /* 7 */ 4 - 3, + /* 8 */ 4 - 1, /* 9 */ 4 - 2, /* a */ 4 - 2, /* b */ 4 - 3, + /* c */ 4 - 2, /* d */ 4 - 3, /* e */ 4 - 3, /* f */ 4 - 4); + const __m256i low_mask = _mm256_set1_epi8(0x0f); + + const __m256i lo = _mm256_and_si256(v, low_mask); + const __m256i hi = _mm256_and_si256(_mm256_srli_epi16(v, 4), low_mask); + const __m256i popcnt1 = _mm256_shuffle_epi8(lookuppos, lo); + const __m256i popcnt2 = _mm256_shuffle_epi8(lookupneg, hi); + return _mm256_sad_epu8(popcnt1, popcnt2); +} + +/** + * Simple CSA over 256 bits + */ +static inline void CSA(__m256i *h, __m256i *l, __m256i a, __m256i b, + __m256i c) { + const __m256i u = _mm256_xor_si256(a, b); + *h = _mm256_or_si256(_mm256_and_si256(a, b), _mm256_and_si256(u, c)); + *l = _mm256_xor_si256(u, c); +} + +/** + * Fast Harley-Seal AVX population count function + */ +inline static uint64_t avx2_harley_seal_popcount256(const __m256i *data, + const uint64_t size) { + __m256i total = _mm256_setzero_si256(); + __m256i ones = _mm256_setzero_si256(); + __m256i twos = _mm256_setzero_si256(); + __m256i fours = _mm256_setzero_si256(); + __m256i eights = _mm256_setzero_si256(); + __m256i sixteens = _mm256_setzero_si256(); + __m256i twosA, twosB, foursA, foursB, eightsA, eightsB; + + const uint64_t limit = size - size % 16; + uint64_t i = 0; + + for (; i < limit; i += 16) { + CSA(&twosA, &ones, ones, _mm256_lddqu_si256(data + i), + _mm256_lddqu_si256(data + i + 1)); + CSA(&twosB, &ones, ones, _mm256_lddqu_si256(data + i + 2), + _mm256_lddqu_si256(data + i + 3)); + CSA(&foursA, &twos, twos, twosA, twosB); + CSA(&twosA, &ones, ones, _mm256_lddqu_si256(data + i + 4), + _mm256_lddqu_si256(data + i + 5)); + CSA(&twosB, &ones, ones, _mm256_lddqu_si256(data + i + 6), + _mm256_lddqu_si256(data + i + 7)); + CSA(&foursB, &twos, twos, twosA, twosB); + CSA(&eightsA, &fours, fours, foursA, foursB); + CSA(&twosA, &ones, ones, _mm256_lddqu_si256(data + i + 8), + _mm256_lddqu_si256(data + i + 9)); + CSA(&twosB, &ones, ones, _mm256_lddqu_si256(data + i + 10), + _mm256_lddqu_si256(data + i + 11)); + CSA(&foursA, &twos, twos, twosA, twosB); + CSA(&twosA, &ones, ones, _mm256_lddqu_si256(data + i + 12), + _mm256_lddqu_si256(data + i + 13)); + CSA(&twosB, &ones, ones, _mm256_lddqu_si256(data + i + 14), + _mm256_lddqu_si256(data + i + 15)); + CSA(&foursB, &twos, twos, twosA, twosB); + CSA(&eightsB, &fours, fours, foursA, foursB); + CSA(&sixteens, &eights, eights, eightsA, eightsB); + + total = _mm256_add_epi64(total, popcount256(sixteens)); + } + + total = _mm256_slli_epi64(total, 4); // * 16 + total = _mm256_add_epi64( + total, _mm256_slli_epi64(popcount256(eights), 3)); // += 8 * ... + total = _mm256_add_epi64( + total, _mm256_slli_epi64(popcount256(fours), 2)); // += 4 * ... + total = _mm256_add_epi64( + total, _mm256_slli_epi64(popcount256(twos), 1)); // += 2 * ... + total = _mm256_add_epi64(total, popcount256(ones)); + for (; i < size; i++) + total = + _mm256_add_epi64(total, popcount256(_mm256_lddqu_si256(data + i))); + + return (uint64_t)(_mm256_extract_epi64(total, 0)) + + (uint64_t)(_mm256_extract_epi64(total, 1)) + + (uint64_t)(_mm256_extract_epi64(total, 2)) + + (uint64_t)(_mm256_extract_epi64(total, 3)); +} + +#define AVXPOPCNTFNC(opname, avx_intrinsic) \ + static inline uint64_t avx2_harley_seal_popcount256_##opname( \ + const __m256i *data1, const __m256i *data2, const uint64_t size) { \ + __m256i total = _mm256_setzero_si256(); \ + __m256i ones = _mm256_setzero_si256(); \ + __m256i twos = _mm256_setzero_si256(); \ + __m256i fours = _mm256_setzero_si256(); \ + __m256i eights = _mm256_setzero_si256(); \ + __m256i sixteens = _mm256_setzero_si256(); \ + __m256i twosA, twosB, foursA, foursB, eightsA, eightsB; \ + __m256i A1, A2; \ + const uint64_t limit = size - size % 16; \ + uint64_t i = 0; \ + for (; i < limit; i += 16) { \ + A1 = avx_intrinsic(_mm256_lddqu_si256(data1 + i), \ + _mm256_lddqu_si256(data2 + i)); \ + A2 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 1), \ + _mm256_lddqu_si256(data2 + i + 1)); \ + CSA(&twosA, &ones, ones, A1, A2); \ + A1 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 2), \ + _mm256_lddqu_si256(data2 + i + 2)); \ + A2 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 3), \ + _mm256_lddqu_si256(data2 + i + 3)); \ + CSA(&twosB, &ones, ones, A1, A2); \ + CSA(&foursA, &twos, twos, twosA, twosB); \ + A1 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 4), \ + _mm256_lddqu_si256(data2 + i + 4)); \ + A2 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 5), \ + _mm256_lddqu_si256(data2 + i + 5)); \ + CSA(&twosA, &ones, ones, A1, A2); \ + A1 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 6), \ + _mm256_lddqu_si256(data2 + i + 6)); \ + A2 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 7), \ + _mm256_lddqu_si256(data2 + i + 7)); \ + CSA(&twosB, &ones, ones, A1, A2); \ + CSA(&foursB, &twos, twos, twosA, twosB); \ + CSA(&eightsA, &fours, fours, foursA, foursB); \ + A1 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 8), \ + _mm256_lddqu_si256(data2 + i + 8)); \ + A2 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 9), \ + _mm256_lddqu_si256(data2 + i + 9)); \ + CSA(&twosA, &ones, ones, A1, A2); \ + A1 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 10), \ + _mm256_lddqu_si256(data2 + i + 10)); \ + A2 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 11), \ + _mm256_lddqu_si256(data2 + i + 11)); \ + CSA(&twosB, &ones, ones, A1, A2); \ + CSA(&foursA, &twos, twos, twosA, twosB); \ + A1 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 12), \ + _mm256_lddqu_si256(data2 + i + 12)); \ + A2 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 13), \ + _mm256_lddqu_si256(data2 + i + 13)); \ + CSA(&twosA, &ones, ones, A1, A2); \ + A1 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 14), \ + _mm256_lddqu_si256(data2 + i + 14)); \ + A2 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 15), \ + _mm256_lddqu_si256(data2 + i + 15)); \ + CSA(&twosB, &ones, ones, A1, A2); \ + CSA(&foursB, &twos, twos, twosA, twosB); \ + CSA(&eightsB, &fours, fours, foursA, foursB); \ + CSA(&sixteens, &eights, eights, eightsA, eightsB); \ + total = _mm256_add_epi64(total, popcount256(sixteens)); \ + } \ + total = _mm256_slli_epi64(total, 4); \ + total = _mm256_add_epi64(total, \ + _mm256_slli_epi64(popcount256(eights), 3)); \ + total = \ + _mm256_add_epi64(total, _mm256_slli_epi64(popcount256(fours), 2)); \ + total = \ + _mm256_add_epi64(total, _mm256_slli_epi64(popcount256(twos), 1)); \ + total = _mm256_add_epi64(total, popcount256(ones)); \ + for (; i < size; i++) { \ + A1 = avx_intrinsic(_mm256_lddqu_si256(data1 + i), \ + _mm256_lddqu_si256(data2 + i)); \ + total = _mm256_add_epi64(total, popcount256(A1)); \ + } \ + return (uint64_t)(_mm256_extract_epi64(total, 0)) + \ + (uint64_t)(_mm256_extract_epi64(total, 1)) + \ + (uint64_t)(_mm256_extract_epi64(total, 2)) + \ + (uint64_t)(_mm256_extract_epi64(total, 3)); \ + } \ + static inline uint64_t avx2_harley_seal_popcount256andstore_##opname( \ + const __m256i *__restrict__ data1, const __m256i *__restrict__ data2, \ + __m256i *__restrict__ out, const uint64_t size) { \ + __m256i total = _mm256_setzero_si256(); \ + __m256i ones = _mm256_setzero_si256(); \ + __m256i twos = _mm256_setzero_si256(); \ + __m256i fours = _mm256_setzero_si256(); \ + __m256i eights = _mm256_setzero_si256(); \ + __m256i sixteens = _mm256_setzero_si256(); \ + __m256i twosA, twosB, foursA, foursB, eightsA, eightsB; \ + __m256i A1, A2; \ + const uint64_t limit = size - size % 16; \ + uint64_t i = 0; \ + for (; i < limit; i += 16) { \ + A1 = avx_intrinsic(_mm256_lddqu_si256(data1 + i), \ + _mm256_lddqu_si256(data2 + i)); \ + _mm256_storeu_si256(out + i, A1); \ + A2 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 1), \ + _mm256_lddqu_si256(data2 + i + 1)); \ + _mm256_storeu_si256(out + i + 1, A2); \ + CSA(&twosA, &ones, ones, A1, A2); \ + A1 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 2), \ + _mm256_lddqu_si256(data2 + i + 2)); \ + _mm256_storeu_si256(out + i + 2, A1); \ + A2 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 3), \ + _mm256_lddqu_si256(data2 + i + 3)); \ + _mm256_storeu_si256(out + i + 3, A2); \ + CSA(&twosB, &ones, ones, A1, A2); \ + CSA(&foursA, &twos, twos, twosA, twosB); \ + A1 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 4), \ + _mm256_lddqu_si256(data2 + i + 4)); \ + _mm256_storeu_si256(out + i + 4, A1); \ + A2 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 5), \ + _mm256_lddqu_si256(data2 + i + 5)); \ + _mm256_storeu_si256(out + i + 5, A2); \ + CSA(&twosA, &ones, ones, A1, A2); \ + A1 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 6), \ + _mm256_lddqu_si256(data2 + i + 6)); \ + _mm256_storeu_si256(out + i + 6, A1); \ + A2 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 7), \ + _mm256_lddqu_si256(data2 + i + 7)); \ + _mm256_storeu_si256(out + i + 7, A2); \ + CSA(&twosB, &ones, ones, A1, A2); \ + CSA(&foursB, &twos, twos, twosA, twosB); \ + CSA(&eightsA, &fours, fours, foursA, foursB); \ + A1 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 8), \ + _mm256_lddqu_si256(data2 + i + 8)); \ + _mm256_storeu_si256(out + i + 8, A1); \ + A2 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 9), \ + _mm256_lddqu_si256(data2 + i + 9)); \ + _mm256_storeu_si256(out + i + 9, A2); \ + CSA(&twosA, &ones, ones, A1, A2); \ + A1 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 10), \ + _mm256_lddqu_si256(data2 + i + 10)); \ + _mm256_storeu_si256(out + i + 10, A1); \ + A2 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 11), \ + _mm256_lddqu_si256(data2 + i + 11)); \ + _mm256_storeu_si256(out + i + 11, A2); \ + CSA(&twosB, &ones, ones, A1, A2); \ + CSA(&foursA, &twos, twos, twosA, twosB); \ + A1 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 12), \ + _mm256_lddqu_si256(data2 + i + 12)); \ + _mm256_storeu_si256(out + i + 12, A1); \ + A2 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 13), \ + _mm256_lddqu_si256(data2 + i + 13)); \ + _mm256_storeu_si256(out + i + 13, A2); \ + CSA(&twosA, &ones, ones, A1, A2); \ + A1 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 14), \ + _mm256_lddqu_si256(data2 + i + 14)); \ + _mm256_storeu_si256(out + i + 14, A1); \ + A2 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 15), \ + _mm256_lddqu_si256(data2 + i + 15)); \ + _mm256_storeu_si256(out + i + 15, A2); \ + CSA(&twosB, &ones, ones, A1, A2); \ + CSA(&foursB, &twos, twos, twosA, twosB); \ + CSA(&eightsB, &fours, fours, foursA, foursB); \ + CSA(&sixteens, &eights, eights, eightsA, eightsB); \ + total = _mm256_add_epi64(total, popcount256(sixteens)); \ + } \ + total = _mm256_slli_epi64(total, 4); \ + total = _mm256_add_epi64(total, \ + _mm256_slli_epi64(popcount256(eights), 3)); \ + total = \ + _mm256_add_epi64(total, _mm256_slli_epi64(popcount256(fours), 2)); \ + total = \ + _mm256_add_epi64(total, _mm256_slli_epi64(popcount256(twos), 1)); \ + total = _mm256_add_epi64(total, popcount256(ones)); \ + for (; i < size; i++) { \ + A1 = avx_intrinsic(_mm256_lddqu_si256(data1 + i), \ + _mm256_lddqu_si256(data2 + i)); \ + _mm256_storeu_si256(out + i, A1); \ + total = _mm256_add_epi64(total, popcount256(A1)); \ + } \ + return (uint64_t)(_mm256_extract_epi64(total, 0)) + \ + (uint64_t)(_mm256_extract_epi64(total, 1)) + \ + (uint64_t)(_mm256_extract_epi64(total, 2)) + \ + (uint64_t)(_mm256_extract_epi64(total, 3)); \ + } + +AVXPOPCNTFNC(or, _mm256_or_si256) +AVXPOPCNTFNC(union, _mm256_or_si256) +AVXPOPCNTFNC(and, _mm256_and_si256) +AVXPOPCNTFNC(intersection, _mm256_and_si256) +AVXPOPCNTFNC (xor, _mm256_xor_si256) +AVXPOPCNTFNC(andnot, _mm256_andnot_si256) + +/*** + * END Harley-Seal popcount functions. + */ + +#endif // USEAVX + +#endif +/* end file include/roaring/bitset_util.h */ +/* begin file include/roaring/containers/array.h */ +/* + * array.h + * + */ + +#ifndef INCLUDE_CONTAINERS_ARRAY_H_ +#define INCLUDE_CONTAINERS_ARRAY_H_ + +#include + + +/* Containers with DEFAULT_MAX_SIZE or less integers should be arrays */ +enum { DEFAULT_MAX_SIZE = 4096 }; + +/* struct array_container - sparse representation of a bitmap + * + * @cardinality: number of indices in `array` (and the bitmap) + * @capacity: allocated size of `array` + * @array: sorted list of integers + */ +struct array_container_s { + int32_t cardinality; + int32_t capacity; + uint16_t *array; +}; + +typedef struct array_container_s array_container_t; + +/* Create a new array with default. Return NULL in case of failure. See also + * array_container_create_given_capacity. */ +array_container_t *array_container_create(void); + +/* Create a new array with a specified capacity size. Return NULL in case of + * failure. */ +array_container_t *array_container_create_given_capacity(int32_t size); + +/* Create a new array containing all values in [min,max). */ +array_container_t * array_container_create_range(uint32_t min, uint32_t max); + +/* + * Shrink the capacity to the actual size, return the number of bytes saved. + */ +int array_container_shrink_to_fit(array_container_t *src); + +/* Free memory owned by `array'. */ +void array_container_free(array_container_t *array); + +/* Duplicate container */ +array_container_t *array_container_clone(const array_container_t *src); + +int32_t array_container_serialize(const array_container_t *container, + char *buf) WARN_UNUSED; + +uint32_t array_container_serialization_len(const array_container_t *container); + +void *array_container_deserialize(const char *buf, size_t buf_len); + +/* Get the cardinality of `array'. */ +static inline int array_container_cardinality(const array_container_t *array) { + return array->cardinality; +} + +static inline bool array_container_nonzero_cardinality( + const array_container_t *array) { + return array->cardinality > 0; +} + +/* Copy one container into another. We assume that they are distinct. */ +void array_container_copy(const array_container_t *src, array_container_t *dst); + +/* Add all the values in [min,max) (included) at a distance k*step from min. + The container must have a size less or equal to DEFAULT_MAX_SIZE after this + addition. */ +void array_container_add_from_range(array_container_t *arr, uint32_t min, + uint32_t max, uint16_t step); + +/* Set the cardinality to zero (does not release memory). */ +static inline void array_container_clear(array_container_t *array) { + array->cardinality = 0; +} + +static inline bool array_container_empty(const array_container_t *array) { + return array->cardinality == 0; +} + +/* check whether the cardinality is equal to the capacity (this does not mean +* that it contains 1<<16 elements) */ +static inline bool array_container_full(const array_container_t *array) { + return array->cardinality == array->capacity; +} + + +/* Compute the union of `src_1' and `src_2' and write the result to `dst' + * It is assumed that `dst' is distinct from both `src_1' and `src_2'. */ +void array_container_union(const array_container_t *src_1, + const array_container_t *src_2, + array_container_t *dst); + +/* symmetric difference, see array_container_union */ +void array_container_xor(const array_container_t *array_1, + const array_container_t *array_2, + array_container_t *out); + +/* Computes the intersection of src_1 and src_2 and write the result to + * dst. It is assumed that dst is distinct from both src_1 and src_2. */ +void array_container_intersection(const array_container_t *src_1, + const array_container_t *src_2, + array_container_t *dst); + +/* Check whether src_1 and src_2 intersect. */ +bool array_container_intersect(const array_container_t *src_1, + const array_container_t *src_2); + + +/* computers the size of the intersection between two arrays. + */ +int array_container_intersection_cardinality(const array_container_t *src_1, + const array_container_t *src_2); + +/* computes the intersection of array1 and array2 and write the result to + * array1. + * */ +void array_container_intersection_inplace(array_container_t *src_1, + const array_container_t *src_2); + +/* + * Write out the 16-bit integers contained in this container as a list of 32-bit + * integers using base + * as the starting value (it might be expected that base has zeros in its 16 + * least significant bits). + * The function returns the number of values written. + * The caller is responsible for allocating enough memory in out. + */ +int array_container_to_uint32_array(void *vout, const array_container_t *cont, + uint32_t base); + +/* Compute the number of runs */ +int32_t array_container_number_of_runs(const array_container_t *a); + +/* + * Print this container using printf (useful for debugging). + */ +void array_container_printf(const array_container_t *v); + +/* + * Print this container using printf as a comma-separated list of 32-bit + * integers starting at base. + */ +void array_container_printf_as_uint32_array(const array_container_t *v, + uint32_t base); + +/** + * Return the serialized size in bytes of a container having cardinality "card". + */ +static inline int32_t array_container_serialized_size_in_bytes(int32_t card) { + return card * 2 + 2; +} + +/** + * Increase capacity to at least min. + * Whether the existing data needs to be copied over depends on the "preserve" + * parameter. If preserve is false, then the new content will be uninitialized, + * otherwise the old content is copied. + */ +void array_container_grow(array_container_t *container, int32_t min, + bool preserve); + +bool array_container_iterate(const array_container_t *cont, uint32_t base, + roaring_iterator iterator, void *ptr); +bool array_container_iterate64(const array_container_t *cont, uint32_t base, + roaring_iterator64 iterator, uint64_t high_bits, + void *ptr); + +/** + * Writes the underlying array to buf, outputs how many bytes were written. + * This is meant to be byte-by-byte compatible with the Java and Go versions of + * Roaring. + * The number of bytes written should be + * array_container_size_in_bytes(container). + * + */ +int32_t array_container_write(const array_container_t *container, char *buf); +/** + * Reads the instance from buf, outputs how many bytes were read. + * This is meant to be byte-by-byte compatible with the Java and Go versions of + * Roaring. + * The number of bytes read should be array_container_size_in_bytes(container). + * You need to provide the (known) cardinality. + */ +int32_t array_container_read(int32_t cardinality, array_container_t *container, + const char *buf); + +/** + * Return the serialized size in bytes of a container (see + * bitset_container_write) + * This is meant to be compatible with the Java and Go versions of Roaring and + * assumes + * that the cardinality of the container is already known. + * + */ +static inline int32_t array_container_size_in_bytes( + const array_container_t *container) { + return container->cardinality * sizeof(uint16_t); +} + +/** + * Return true if the two arrays have the same content. + */ +static inline bool array_container_equals( + const array_container_t *container1, + const array_container_t *container2) { + + if (container1->cardinality != container2->cardinality) { + return false; + } + return memequals(container1->array, container2->array, container1->cardinality*2); +} + +/** + * Return true if container1 is a subset of container2. + */ +bool array_container_is_subset(const array_container_t *container1, + const array_container_t *container2); + +/** + * If the element of given rank is in this container, supposing that the first + * element has rank start_rank, then the function returns true and sets element + * accordingly. + * Otherwise, it returns false and update start_rank. + */ +static inline bool array_container_select(const array_container_t *container, + uint32_t *start_rank, uint32_t rank, + uint32_t *element) { + int card = array_container_cardinality(container); + if (*start_rank + card <= rank) { + *start_rank += card; + return false; + } else { + *element = container->array[rank - *start_rank]; + return true; + } +} + +/* Computes the difference of array1 and array2 and write the result + * to array out. + * Array out does not need to be distinct from array_1 + */ +void array_container_andnot(const array_container_t *array_1, + const array_container_t *array_2, + array_container_t *out); + +/* Append x to the set. Assumes that the value is larger than any preceding + * values. */ +static inline void array_container_append(array_container_t *arr, + uint16_t pos) { + const int32_t capacity = arr->capacity; + + if (array_container_full(arr)) { + array_container_grow(arr, capacity + 1, true); + } + + arr->array[arr->cardinality++] = pos; +} + +/** + * Add value to the set if final cardinality doesn't exceed max_cardinality. + * Return code: + * 1 -- value was added + * 0 -- value was already present + * -1 -- value was not added because cardinality would exceed max_cardinality + */ +static inline int array_container_try_add(array_container_t *arr, uint16_t value, + int32_t max_cardinality) { + const int32_t cardinality = arr->cardinality; + + // best case, we can append. + if ((array_container_empty(arr) || arr->array[cardinality - 1] < value) && + cardinality < max_cardinality) { + array_container_append(arr, value); + return 1; + } + + const int32_t loc = binarySearch(arr->array, cardinality, value); + + if (loc >= 0) { + return 0; + } else if (cardinality < max_cardinality) { + if (array_container_full(arr)) { + array_container_grow(arr, arr->capacity + 1, true); + } + const int32_t insert_idx = -loc - 1; + memmove(arr->array + insert_idx + 1, arr->array + insert_idx, + (cardinality - insert_idx) * sizeof(uint16_t)); + arr->array[insert_idx] = value; + arr->cardinality++; + return 1; + } else { + return -1; + } +} + +/* Add value to the set. Returns true if x was not already present. */ +static inline bool array_container_add(array_container_t *arr, uint16_t value) { + return array_container_try_add(arr, value, INT32_MAX) == 1; +} + +/* Remove x from the set. Returns true if x was present. */ +static inline bool array_container_remove(array_container_t *arr, + uint16_t pos) { + const int32_t idx = binarySearch(arr->array, arr->cardinality, pos); + const bool is_present = idx >= 0; + if (is_present) { + memmove(arr->array + idx, arr->array + idx + 1, + (arr->cardinality - idx - 1) * sizeof(uint16_t)); + arr->cardinality--; + } + + return is_present; +} + +/* Check whether x is present. */ +static inline bool array_container_contains(const array_container_t *arr, + uint16_t pos) { + // return binarySearch(arr->array, arr->cardinality, pos) >= 0; + // binary search with fallback to linear search for short ranges + int32_t low = 0; + const uint16_t * carr = (const uint16_t *) arr->array; + int32_t high = arr->cardinality - 1; + // while (high - low >= 0) { + while(high >= low + 16) { + int32_t middleIndex = (low + high)>>1; + uint16_t middleValue = carr[middleIndex]; + if (middleValue < pos) { + low = middleIndex + 1; + } else if (middleValue > pos) { + high = middleIndex - 1; + } else { + return true; + } + } + + for (int i=low; i <= high; i++) { + uint16_t v = carr[i]; + if (v == pos) { + return true; + } + if ( v > pos ) return false; + } + return false; + +} + +//* Check whether a range of values from range_start (included) to range_end (excluded) is present. */ +static inline bool array_container_contains_range(const array_container_t *arr, + uint32_t range_start, uint32_t range_end) { + + const uint16_t rs_included = range_start; + const uint16_t re_included = range_end - 1; + + const uint16_t *carr = (const uint16_t *) arr->array; + + const int32_t start = advanceUntil(carr, -1, arr->cardinality, rs_included); + const int32_t end = advanceUntil(carr, start - 1, arr->cardinality, re_included); + + return (start < arr->cardinality) && (end < arr->cardinality) + && (((uint16_t)(end - start)) == re_included - rs_included) + && (carr[start] == rs_included) && (carr[end] == re_included); +} + +/* Returns the smallest value (assumes not empty) */ +static inline uint16_t array_container_minimum(const array_container_t *arr) { + if (arr->cardinality == 0) return 0; + return arr->array[0]; +} + +/* Returns the largest value (assumes not empty) */ +static inline uint16_t array_container_maximum(const array_container_t *arr) { + if (arr->cardinality == 0) return 0; + return arr->array[arr->cardinality - 1]; +} + +/* Returns the number of values equal or smaller than x */ +static inline int array_container_rank(const array_container_t *arr, uint16_t x) { + const int32_t idx = binarySearch(arr->array, arr->cardinality, x); + const bool is_present = idx >= 0; + if (is_present) { + return idx + 1; + } else { + return -idx - 1; + } +} + +/* Returns the index of the first value equal or smaller than x, or -1 */ +static inline int array_container_index_equalorlarger(const array_container_t *arr, uint16_t x) { + const int32_t idx = binarySearch(arr->array, arr->cardinality, x); + const bool is_present = idx >= 0; + if (is_present) { + return idx; + } else { + int32_t candidate = - idx - 1; + if(candidate < arr->cardinality) return candidate; + return -1; + } +} + +/* + * Adds all values in range [min,max] using hint: + * nvals_less is the number of array values less than $min + * nvals_greater is the number of array values greater than $max + */ +static inline void array_container_add_range_nvals(array_container_t *array, + uint32_t min, uint32_t max, + int32_t nvals_less, + int32_t nvals_greater) { + int32_t union_cardinality = nvals_less + (max - min + 1) + nvals_greater; + if (union_cardinality > array->capacity) { + array_container_grow(array, union_cardinality, true); + } + memmove(&(array->array[union_cardinality - nvals_greater]), + &(array->array[array->cardinality - nvals_greater]), + nvals_greater * sizeof(uint16_t)); + for (uint32_t i = 0; i <= max - min; i++) { + array->array[nvals_less + i] = min + i; + } + array->cardinality = union_cardinality; +} + +/** + * Adds all values in range [min,max]. + */ +static inline void array_container_add_range(array_container_t *array, + uint32_t min, uint32_t max) { + int32_t nvals_greater = count_greater(array->array, array->cardinality, max); + int32_t nvals_less = count_less(array->array, array->cardinality - nvals_greater, min); + array_container_add_range_nvals(array, min, max, nvals_less, nvals_greater); +} + +/* + * Removes all elements array[pos] .. array[pos+count-1] + */ +static inline void array_container_remove_range(array_container_t *array, + uint32_t pos, uint32_t count) { + if (count != 0) { + memmove(&(array->array[pos]), &(array->array[pos+count]), + (array->cardinality - pos - count) * sizeof(uint16_t)); + array->cardinality -= count; + } +} + +#endif /* INCLUDE_CONTAINERS_ARRAY_H_ */ +/* end file include/roaring/containers/array.h */ +/* begin file include/roaring/containers/bitset.h */ +/* + * bitset.h + * + */ + +#ifndef INCLUDE_CONTAINERS_BITSET_H_ +#define INCLUDE_CONTAINERS_BITSET_H_ + +#include +#include + +#ifdef USEAVX +#define ALIGN_AVX __attribute__((aligned(sizeof(__m256i)))) +#else +#define ALIGN_AVX +#endif + +enum { + BITSET_CONTAINER_SIZE_IN_WORDS = (1 << 16) / 64, + BITSET_UNKNOWN_CARDINALITY = -1 +}; + +struct bitset_container_s { + int32_t cardinality; + uint64_t *array; +}; + +typedef struct bitset_container_s bitset_container_t; + +/* Create a new bitset. Return NULL in case of failure. */ +bitset_container_t *bitset_container_create(void); + +/* Free memory. */ +void bitset_container_free(bitset_container_t *bitset); + +/* Clear bitset (sets bits to 0). */ +void bitset_container_clear(bitset_container_t *bitset); + +/* Set all bits to 1. */ +void bitset_container_set_all(bitset_container_t *bitset); + +/* Duplicate bitset */ +bitset_container_t *bitset_container_clone(const bitset_container_t *src); + +int32_t bitset_container_serialize(const bitset_container_t *container, + char *buf) WARN_UNUSED; + +uint32_t bitset_container_serialization_len(void); + +void *bitset_container_deserialize(const char *buf, size_t buf_len); + +/* Set the bit in [begin,end). WARNING: as of April 2016, this method is slow + * and + * should not be used in performance-sensitive code. Ever. */ +void bitset_container_set_range(bitset_container_t *bitset, uint32_t begin, + uint32_t end); + +#ifdef ASMBITMANIPOPTIMIZATION +/* Set the ith bit. */ +static inline void bitset_container_set(bitset_container_t *bitset, + uint16_t pos) { + uint64_t shift = 6; + uint64_t offset; + uint64_t p = pos; + ASM_SHIFT_RIGHT(p, shift, offset); + uint64_t load = bitset->array[offset]; + ASM_SET_BIT_INC_WAS_CLEAR(load, p, bitset->cardinality); + bitset->array[offset] = load; +} + +/* Unset the ith bit. */ +static inline void bitset_container_unset(bitset_container_t *bitset, + uint16_t pos) { + uint64_t shift = 6; + uint64_t offset; + uint64_t p = pos; + ASM_SHIFT_RIGHT(p, shift, offset); + uint64_t load = bitset->array[offset]; + ASM_CLEAR_BIT_DEC_WAS_SET(load, p, bitset->cardinality); + bitset->array[offset] = load; +} + +/* Add `pos' to `bitset'. Returns true if `pos' was not present. Might be slower + * than bitset_container_set. */ +static inline bool bitset_container_add(bitset_container_t *bitset, + uint16_t pos) { + uint64_t shift = 6; + uint64_t offset; + uint64_t p = pos; + ASM_SHIFT_RIGHT(p, shift, offset); + uint64_t load = bitset->array[offset]; + // could be possibly slightly further optimized + const int32_t oldcard = bitset->cardinality; + ASM_SET_BIT_INC_WAS_CLEAR(load, p, bitset->cardinality); + bitset->array[offset] = load; + return bitset->cardinality - oldcard; +} + +/* Remove `pos' from `bitset'. Returns true if `pos' was present. Might be + * slower than bitset_container_unset. */ +static inline bool bitset_container_remove(bitset_container_t *bitset, + uint16_t pos) { + uint64_t shift = 6; + uint64_t offset; + uint64_t p = pos; + ASM_SHIFT_RIGHT(p, shift, offset); + uint64_t load = bitset->array[offset]; + // could be possibly slightly further optimized + const int32_t oldcard = bitset->cardinality; + ASM_CLEAR_BIT_DEC_WAS_SET(load, p, bitset->cardinality); + bitset->array[offset] = load; + return oldcard - bitset->cardinality; +} + +/* Get the value of the ith bit. */ +static inline bool bitset_container_get(const bitset_container_t *bitset, + uint16_t pos) { + uint64_t word = bitset->array[pos >> 6]; + const uint64_t p = pos; + ASM_INPLACESHIFT_RIGHT(word, p); + return word & 1; +} + +#else + +/* Set the ith bit. */ +static inline void bitset_container_set(bitset_container_t *bitset, + uint16_t pos) { + const uint64_t old_word = bitset->array[pos >> 6]; + const int index = pos & 63; + const uint64_t new_word = old_word | (UINT64_C(1) << index); + bitset->cardinality += (uint32_t)((old_word ^ new_word) >> index); + bitset->array[pos >> 6] = new_word; +} + +/* Unset the ith bit. */ +static inline void bitset_container_unset(bitset_container_t *bitset, + uint16_t pos) { + const uint64_t old_word = bitset->array[pos >> 6]; + const int index = pos & 63; + const uint64_t new_word = old_word & (~(UINT64_C(1) << index)); + bitset->cardinality -= (uint32_t)((old_word ^ new_word) >> index); + bitset->array[pos >> 6] = new_word; +} + +/* Add `pos' to `bitset'. Returns true if `pos' was not present. Might be slower + * than bitset_container_set. */ +static inline bool bitset_container_add(bitset_container_t *bitset, + uint16_t pos) { + const uint64_t old_word = bitset->array[pos >> 6]; + const int index = pos & 63; + const uint64_t new_word = old_word | (UINT64_C(1) << index); + const uint64_t increment = (old_word ^ new_word) >> index; + bitset->cardinality += (uint32_t)increment; + bitset->array[pos >> 6] = new_word; + return increment > 0; +} + +/* Remove `pos' from `bitset'. Returns true if `pos' was present. Might be + * slower than bitset_container_unset. */ +static inline bool bitset_container_remove(bitset_container_t *bitset, + uint16_t pos) { + const uint64_t old_word = bitset->array[pos >> 6]; + const int index = pos & 63; + const uint64_t new_word = old_word & (~(UINT64_C(1) << index)); + const uint64_t increment = (old_word ^ new_word) >> index; + bitset->cardinality -= (uint32_t)increment; + bitset->array[pos >> 6] = new_word; + return increment > 0; +} + +/* Get the value of the ith bit. */ +static inline bool bitset_container_get(const bitset_container_t *bitset, + uint16_t pos) { + const uint64_t word = bitset->array[pos >> 6]; + return (word >> (pos & 63)) & 1; +} + +#endif + +/* +* Check if all bits are set in a range of positions from pos_start (included) to +* pos_end (excluded). +*/ +static inline bool bitset_container_get_range(const bitset_container_t *bitset, + uint32_t pos_start, uint32_t pos_end) { + + const uint32_t start = pos_start >> 6; + const uint32_t end = pos_end >> 6; + + const uint64_t first = ~((1ULL << (pos_start & 0x3F)) - 1); + const uint64_t last = (1ULL << (pos_end & 0x3F)) - 1; + + if (start == end) return ((bitset->array[end] & first & last) == (first & last)); + if ((bitset->array[start] & first) != first) return false; + + if ((end < BITSET_CONTAINER_SIZE_IN_WORDS) && ((bitset->array[end] & last) != last)){ + + return false; + } + + for (uint16_t i = start + 1; (i < BITSET_CONTAINER_SIZE_IN_WORDS) && (i < end); ++i){ + + if (bitset->array[i] != UINT64_C(0xFFFFFFFFFFFFFFFF)) return false; + } + + return true; +} + +/* Check whether `bitset' is present in `array'. Calls bitset_container_get. */ +static inline bool bitset_container_contains(const bitset_container_t *bitset, + uint16_t pos) { + return bitset_container_get(bitset, pos); +} + +/* +* Check whether a range of bits from position `pos_start' (included) to `pos_end' (excluded) +* is present in `bitset'. Calls bitset_container_get_all. +*/ +static inline bool bitset_container_contains_range(const bitset_container_t *bitset, + uint32_t pos_start, uint32_t pos_end) { + return bitset_container_get_range(bitset, pos_start, pos_end); +} + +/* Get the number of bits set */ +static inline int bitset_container_cardinality( + const bitset_container_t *bitset) { + return bitset->cardinality; +} + + + + +/* Copy one container into another. We assume that they are distinct. */ +void bitset_container_copy(const bitset_container_t *source, + bitset_container_t *dest); + +/* Add all the values [min,max) at a distance k*step from min: min, + * min+step,.... */ +void bitset_container_add_from_range(bitset_container_t *bitset, uint32_t min, + uint32_t max, uint16_t step); + +/* Get the number of bits set (force computation). This does not modify bitset. + * To update the cardinality, you should do + * bitset->cardinality = bitset_container_compute_cardinality(bitset).*/ +int bitset_container_compute_cardinality(const bitset_container_t *bitset); + +/* Get whether there is at least one bit set (see bitset_container_empty for the reverse), + when the cardinality is unknown, it is computed and stored in the struct */ +static inline bool bitset_container_nonzero_cardinality( + bitset_container_t *bitset) { + // account for laziness + if (bitset->cardinality == BITSET_UNKNOWN_CARDINALITY) { + // could bail early instead with a nonzero result + bitset->cardinality = bitset_container_compute_cardinality(bitset); + } + return bitset->cardinality > 0; +} + +/* Check whether this bitset is empty (see bitset_container_nonzero_cardinality for the reverse), + * it never modifies the bitset struct. */ +static inline bool bitset_container_empty( + const bitset_container_t *bitset) { + if (bitset->cardinality == BITSET_UNKNOWN_CARDINALITY) { + for (int i = 0; i < BITSET_CONTAINER_SIZE_IN_WORDS; i ++) { + if((bitset->array[i]) != 0) return false; + } + return true; + } + return bitset->cardinality == 0; +} + + +/* Get whether there is at least one bit set (see bitset_container_empty for the reverse), + the bitset is never modified */ +static inline bool bitset_container_const_nonzero_cardinality( + const bitset_container_t *bitset) { + return !bitset_container_empty(bitset); +} + +/* + * Check whether the two bitsets intersect + */ +bool bitset_container_intersect(const bitset_container_t *src_1, + const bitset_container_t *src_2); + +/* Computes the union of bitsets `src_1' and `src_2' into `dst' and return the + * cardinality. */ +int bitset_container_or(const bitset_container_t *src_1, + const bitset_container_t *src_2, + bitset_container_t *dst); + +/* Computes the union of bitsets `src_1' and `src_2' and return the cardinality. + */ +int bitset_container_or_justcard(const bitset_container_t *src_1, + const bitset_container_t *src_2); + +/* Computes the union of bitsets `src_1' and `src_2' into `dst' and return the + * cardinality. Same as bitset_container_or. */ +int bitset_container_union(const bitset_container_t *src_1, + const bitset_container_t *src_2, + bitset_container_t *dst); + +/* Computes the union of bitsets `src_1' and `src_2' and return the + * cardinality. Same as bitset_container_or_justcard. */ +int bitset_container_union_justcard(const bitset_container_t *src_1, + const bitset_container_t *src_2); + +/* Computes the union of bitsets `src_1' and `src_2' into `dst', but does not + * update the cardinality. Provided to optimize chained operations. */ +int bitset_container_or_nocard(const bitset_container_t *src_1, + const bitset_container_t *src_2, + bitset_container_t *dst); + +/* Computes the union of bitsets `src_1' and `src_2' into `dst', but does not + * update the cardinality. Same as bitset_container_or_nocard */ +int bitset_container_union_nocard(const bitset_container_t *src_1, + const bitset_container_t *src_2, + bitset_container_t *dst); + +/* Computes the intersection of bitsets `src_1' and `src_2' into `dst' and + * return the cardinality. */ +int bitset_container_and(const bitset_container_t *src_1, + const bitset_container_t *src_2, + bitset_container_t *dst); + +/* Computes the intersection of bitsets `src_1' and `src_2' and return the + * cardinality. */ +int bitset_container_and_justcard(const bitset_container_t *src_1, + const bitset_container_t *src_2); + +/* Computes the intersection of bitsets `src_1' and `src_2' into `dst' and + * return the cardinality. Same as bitset_container_and. */ +int bitset_container_intersection(const bitset_container_t *src_1, + const bitset_container_t *src_2, + bitset_container_t *dst); + +/* Computes the intersection of bitsets `src_1' and `src_2' and return the + * cardinality. Same as bitset_container_and_justcard. */ +int bitset_container_intersection_justcard(const bitset_container_t *src_1, + const bitset_container_t *src_2); + +/* Computes the intersection of bitsets `src_1' and `src_2' into `dst', but does + * not update the cardinality. Provided to optimize chained operations. */ +int bitset_container_and_nocard(const bitset_container_t *src_1, + const bitset_container_t *src_2, + bitset_container_t *dst); + +/* Computes the intersection of bitsets `src_1' and `src_2' into `dst', but does + * not update the cardinality. Same as bitset_container_and_nocard */ +int bitset_container_intersection_nocard(const bitset_container_t *src_1, + const bitset_container_t *src_2, + bitset_container_t *dst); + +/* Computes the exclusive or of bitsets `src_1' and `src_2' into `dst' and + * return the cardinality. */ +int bitset_container_xor(const bitset_container_t *src_1, + const bitset_container_t *src_2, + bitset_container_t *dst); + +/* Computes the exclusive or of bitsets `src_1' and `src_2' and return the + * cardinality. */ +int bitset_container_xor_justcard(const bitset_container_t *src_1, + const bitset_container_t *src_2); + +/* Computes the exclusive or of bitsets `src_1' and `src_2' into `dst', but does + * not update the cardinality. Provided to optimize chained operations. */ +int bitset_container_xor_nocard(const bitset_container_t *src_1, + const bitset_container_t *src_2, + bitset_container_t *dst); + +/* Computes the and not of bitsets `src_1' and `src_2' into `dst' and return the + * cardinality. */ +int bitset_container_andnot(const bitset_container_t *src_1, + const bitset_container_t *src_2, + bitset_container_t *dst); + +/* Computes the and not of bitsets `src_1' and `src_2' and return the + * cardinality. */ +int bitset_container_andnot_justcard(const bitset_container_t *src_1, + const bitset_container_t *src_2); + +/* Computes the and not or of bitsets `src_1' and `src_2' into `dst', but does + * not update the cardinality. Provided to optimize chained operations. */ +int bitset_container_andnot_nocard(const bitset_container_t *src_1, + const bitset_container_t *src_2, + bitset_container_t *dst); + +/* + * Write out the 16-bit integers contained in this container as a list of 32-bit + * integers using base + * as the starting value (it might be expected that base has zeros in its 16 + * least significant bits). + * The function returns the number of values written. + * The caller is responsible for allocating enough memory in out. + * The out pointer should point to enough memory (the cardinality times 32 + * bits). + */ +int bitset_container_to_uint32_array(void *out, const bitset_container_t *cont, + uint32_t base); + +/* + * Print this container using printf (useful for debugging). + */ +void bitset_container_printf(const bitset_container_t *v); + +/* + * Print this container using printf as a comma-separated list of 32-bit + * integers starting at base. + */ +void bitset_container_printf_as_uint32_array(const bitset_container_t *v, + uint32_t base); + +/** + * Return the serialized size in bytes of a container. + */ +static inline int32_t bitset_container_serialized_size_in_bytes(void) { + return BITSET_CONTAINER_SIZE_IN_WORDS * 8; +} + +/** + * Return the the number of runs. + */ +int bitset_container_number_of_runs(bitset_container_t *b); + +bool bitset_container_iterate(const bitset_container_t *cont, uint32_t base, + roaring_iterator iterator, void *ptr); +bool bitset_container_iterate64(const bitset_container_t *cont, uint32_t base, + roaring_iterator64 iterator, uint64_t high_bits, + void *ptr); + +/** + * Writes the underlying array to buf, outputs how many bytes were written. + * This is meant to be byte-by-byte compatible with the Java and Go versions of + * Roaring. + * The number of bytes written should be + * bitset_container_size_in_bytes(container). + */ +int32_t bitset_container_write(const bitset_container_t *container, char *buf); + +/** + * Reads the instance from buf, outputs how many bytes were read. + * This is meant to be byte-by-byte compatible with the Java and Go versions of + * Roaring. + * The number of bytes read should be bitset_container_size_in_bytes(container). + * You need to provide the (known) cardinality. + */ +int32_t bitset_container_read(int32_t cardinality, + bitset_container_t *container, const char *buf); +/** + * Return the serialized size in bytes of a container (see + * bitset_container_write). + * This is meant to be compatible with the Java and Go versions of Roaring and + * assumes + * that the cardinality of the container is already known or can be computed. + */ +static inline int32_t bitset_container_size_in_bytes( + const bitset_container_t *container) { + (void)container; + return BITSET_CONTAINER_SIZE_IN_WORDS * sizeof(uint64_t); +} + +/** + * Return true if the two containers have the same content. + */ +bool bitset_container_equals(const bitset_container_t *container1, + const bitset_container_t *container2); + +/** +* Return true if container1 is a subset of container2. +*/ +bool bitset_container_is_subset(const bitset_container_t *container1, + const bitset_container_t *container2); + +/** + * If the element of given rank is in this container, supposing that the first + * element has rank start_rank, then the function returns true and sets element + * accordingly. + * Otherwise, it returns false and update start_rank. + */ +bool bitset_container_select(const bitset_container_t *container, + uint32_t *start_rank, uint32_t rank, + uint32_t *element); + +/* Returns the smallest value (assumes not empty) */ +uint16_t bitset_container_minimum(const bitset_container_t *container); + +/* Returns the largest value (assumes not empty) */ +uint16_t bitset_container_maximum(const bitset_container_t *container); + +/* Returns the number of values equal or smaller than x */ +int bitset_container_rank(const bitset_container_t *container, uint16_t x); + +/* Returns the index of the first value equal or larger than x, or -1 */ +int bitset_container_index_equalorlarger(const bitset_container_t *container, uint16_t x); +#endif /* INCLUDE_CONTAINERS_BITSET_H_ */ +/* end file include/roaring/containers/bitset.h */ +/* begin file include/roaring/containers/run.h */ +/* + * run.h + * + */ + +#ifndef INCLUDE_CONTAINERS_RUN_H_ +#define INCLUDE_CONTAINERS_RUN_H_ + +#include +#include +#include +#include + + +/* struct rle16_s - run length pair + * + * @value: start position of the run + * @length: length of the run is `length + 1` + * + * An RLE pair {v, l} would represent the integers between the interval + * [v, v+l+1], e.g. {3, 2} = [3, 4, 5]. + */ +struct rle16_s { + uint16_t value; + uint16_t length; +}; + +typedef struct rle16_s rle16_t; + +/* struct run_container_s - run container bitmap + * + * @n_runs: number of rle_t pairs in `runs`. + * @capacity: capacity in rle_t pairs `runs` can hold. + * @runs: pairs of rle_t. + * + */ +struct run_container_s { + int32_t n_runs; + int32_t capacity; + rle16_t *runs; +}; + +typedef struct run_container_s run_container_t; + +/* Create a new run container. Return NULL in case of failure. */ +run_container_t *run_container_create(void); + +/* Create a new run container with given capacity. Return NULL in case of + * failure. */ +run_container_t *run_container_create_given_capacity(int32_t size); + +/* + * Shrink the capacity to the actual size, return the number of bytes saved. + */ +int run_container_shrink_to_fit(run_container_t *src); + +/* Free memory owned by `run'. */ +void run_container_free(run_container_t *run); + +/* Duplicate container */ +run_container_t *run_container_clone(const run_container_t *src); + +int32_t run_container_serialize(const run_container_t *container, + char *buf) WARN_UNUSED; + +uint32_t run_container_serialization_len(const run_container_t *container); + +void *run_container_deserialize(const char *buf, size_t buf_len); + +/* + * Effectively deletes the value at index index, repacking data. + */ +static inline void recoverRoomAtIndex(run_container_t *run, uint16_t index) { + memmove(run->runs + index, run->runs + (1 + index), + (run->n_runs - index - 1) * sizeof(rle16_t)); + run->n_runs--; +} + +/** + * Good old binary search through rle data + */ +static inline int32_t interleavedBinarySearch(const rle16_t *array, int32_t lenarray, + uint16_t ikey) { + int32_t low = 0; + int32_t high = lenarray - 1; + while (low <= high) { + int32_t middleIndex = (low + high) >> 1; + uint16_t middleValue = array[middleIndex].value; + if (middleValue < ikey) { + low = middleIndex + 1; + } else if (middleValue > ikey) { + high = middleIndex - 1; + } else { + return middleIndex; + } + } + return -(low + 1); +} + +/* + * Returns index of the run which contains $ikey + */ +static inline int32_t rle16_find_run(const rle16_t *array, int32_t lenarray, + uint16_t ikey) { + int32_t low = 0; + int32_t high = lenarray - 1; + while (low <= high) { + int32_t middleIndex = (low + high) >> 1; + uint16_t min = array[middleIndex].value; + uint16_t max = array[middleIndex].value + array[middleIndex].length; + if (ikey > max) { + low = middleIndex + 1; + } else if (ikey < min) { + high = middleIndex - 1; + } else { + return middleIndex; + } + } + return -(low + 1); +} + + +/** + * Returns number of runs which can'be be merged with the key because they + * are less than the key. + * Note that [5,6,7,8] can be merged with the key 9 and won't be counted. + */ +static inline int32_t rle16_count_less(const rle16_t* array, int32_t lenarray, + uint16_t key) { + if (lenarray == 0) return 0; + int32_t low = 0; + int32_t high = lenarray - 1; + while (low <= high) { + int32_t middleIndex = (low + high) >> 1; + uint16_t min_value = array[middleIndex].value; + uint16_t max_value = array[middleIndex].value + array[middleIndex].length; + if (max_value + UINT32_C(1) < key) { // uint32 arithmetic + low = middleIndex + 1; + } else if (key < min_value) { + high = middleIndex - 1; + } else { + return middleIndex; + } + } + return low; +} + +static inline int32_t rle16_count_greater(const rle16_t* array, int32_t lenarray, + uint16_t key) { + if (lenarray == 0) return 0; + int32_t low = 0; + int32_t high = lenarray - 1; + while (low <= high) { + int32_t middleIndex = (low + high) >> 1; + uint16_t min_value = array[middleIndex].value; + uint16_t max_value = array[middleIndex].value + array[middleIndex].length; + if (max_value < key) { + low = middleIndex + 1; + } else if (key + UINT32_C(1) < min_value) { // uint32 arithmetic + high = middleIndex - 1; + } else { + return lenarray - (middleIndex + 1); + } + } + return lenarray - low; +} + +/** + * increase capacity to at least min. Whether the + * existing data needs to be copied over depends on copy. If "copy" is false, + * then the new content will be uninitialized, otherwise a copy is made. + */ +void run_container_grow(run_container_t *run, int32_t min, bool copy); + +/** + * Moves the data so that we can write data at index + */ +static inline void makeRoomAtIndex(run_container_t *run, uint16_t index) { + /* This function calls realloc + memmove sequentially to move by one index. + * Potentially copying twice the array. + */ + if (run->n_runs + 1 > run->capacity) + run_container_grow(run, run->n_runs + 1, true); + memmove(run->runs + 1 + index, run->runs + index, + (run->n_runs - index) * sizeof(rle16_t)); + run->n_runs++; +} + +/* Add `pos' to `run'. Returns true if `pos' was not present. */ +bool run_container_add(run_container_t *run, uint16_t pos); + +/* Remove `pos' from `run'. Returns true if `pos' was present. */ +static inline bool run_container_remove(run_container_t *run, uint16_t pos) { + int32_t index = interleavedBinarySearch(run->runs, run->n_runs, pos); + if (index >= 0) { + int32_t le = run->runs[index].length; + if (le == 0) { + recoverRoomAtIndex(run, (uint16_t)index); + } else { + run->runs[index].value++; + run->runs[index].length--; + } + return true; + } + index = -index - 2; // points to preceding value, possibly -1 + if (index >= 0) { // possible match + int32_t offset = pos - run->runs[index].value; + int32_t le = run->runs[index].length; + if (offset < le) { + // need to break in two + run->runs[index].length = (uint16_t)(offset - 1); + // need to insert + uint16_t newvalue = pos + 1; + int32_t newlength = le - offset - 1; + makeRoomAtIndex(run, (uint16_t)(index + 1)); + run->runs[index + 1].value = newvalue; + run->runs[index + 1].length = (uint16_t)newlength; + return true; + + } else if (offset == le) { + run->runs[index].length--; + return true; + } + } + // no match + return false; +} + +/* Check whether `pos' is present in `run'. */ +static inline bool run_container_contains(const run_container_t *run, uint16_t pos) { + int32_t index = interleavedBinarySearch(run->runs, run->n_runs, pos); + if (index >= 0) return true; + index = -index - 2; // points to preceding value, possibly -1 + if (index != -1) { // possible match + int32_t offset = pos - run->runs[index].value; + int32_t le = run->runs[index].length; + if (offset <= le) return true; + } + return false; +} + +/* +* Check whether all positions in a range of positions from pos_start (included) +* to pos_end (excluded) is present in `run'. +*/ +static inline bool run_container_contains_range(const run_container_t *run, + uint32_t pos_start, uint32_t pos_end) { + uint32_t count = 0; + int32_t index = interleavedBinarySearch(run->runs, run->n_runs, pos_start); + if (index < 0) { + index = -index - 2; + if ((index == -1) || ((pos_start - run->runs[index].value) > run->runs[index].length)){ + return false; + } + } + for (int32_t i = index; i < run->n_runs; ++i) { + const uint32_t stop = run->runs[i].value + run->runs[i].length; + if (run->runs[i].value >= pos_end) break; + if (stop >= pos_end) { + count += (((pos_end - run->runs[i].value) > 0) ? (pos_end - run->runs[i].value) : 0); + break; + } + const uint32_t min = (stop - pos_start) > 0 ? (stop - pos_start) : 0; + count += (min < run->runs[i].length) ? min : run->runs[i].length; + } + return count >= (pos_end - pos_start - 1); +} + +#ifdef USEAVX + +/* Get the cardinality of `run'. Requires an actual computation. */ +static inline int run_container_cardinality(const run_container_t *run) { + const int32_t n_runs = run->n_runs; + const rle16_t *runs = run->runs; + + /* by initializing with n_runs, we omit counting the +1 for each pair. */ + int sum = n_runs; + int32_t k = 0; + const int32_t step = sizeof(__m256i) / sizeof(rle16_t); + if (n_runs > step) { + __m256i total = _mm256_setzero_si256(); + for (; k + step <= n_runs; k += step) { + __m256i ymm1 = _mm256_lddqu_si256((const __m256i *)(runs + k)); + __m256i justlengths = _mm256_srli_epi32(ymm1, 16); + total = _mm256_add_epi32(total, justlengths); + } + // a store might be faster than extract? + uint32_t buffer[sizeof(__m256i) / sizeof(rle16_t)]; + _mm256_storeu_si256((__m256i *)buffer, total); + sum += (buffer[0] + buffer[1]) + (buffer[2] + buffer[3]) + + (buffer[4] + buffer[5]) + (buffer[6] + buffer[7]); + } + for (; k < n_runs; ++k) { + sum += runs[k].length; + } + + return sum; +} + +#else + +/* Get the cardinality of `run'. Requires an actual computation. */ +static inline int run_container_cardinality(const run_container_t *run) { + const int32_t n_runs = run->n_runs; + const rle16_t *runs = run->runs; + + /* by initializing with n_runs, we omit counting the +1 for each pair. */ + int sum = n_runs; + for (int k = 0; k < n_runs; ++k) { + sum += runs[k].length; + } + + return sum; +} +#endif + +/* Card > 0?, see run_container_empty for the reverse */ +static inline bool run_container_nonzero_cardinality( + const run_container_t *run) { + return run->n_runs > 0; // runs never empty +} + +/* Card == 0?, see run_container_nonzero_cardinality for the reverse */ +static inline bool run_container_empty( + const run_container_t *run) { + return run->n_runs == 0; // runs never empty +} + + + +/* Copy one container into another. We assume that they are distinct. */ +void run_container_copy(const run_container_t *src, run_container_t *dst); + +/* Set the cardinality to zero (does not release memory). */ +static inline void run_container_clear(run_container_t *run) { + run->n_runs = 0; +} + +/** + * Append run described by vl to the run container, possibly merging. + * It is assumed that the run would be inserted at the end of the container, no + * check is made. + * It is assumed that the run container has the necessary capacity: caller is + * responsible for checking memory capacity. + * + * + * This is not a safe function, it is meant for performance: use with care. + */ +static inline void run_container_append(run_container_t *run, rle16_t vl, + rle16_t *previousrl) { + const uint32_t previousend = previousrl->value + previousrl->length; + if (vl.value > previousend + 1) { // we add a new one + run->runs[run->n_runs] = vl; + run->n_runs++; + *previousrl = vl; + } else { + uint32_t newend = vl.value + vl.length + UINT32_C(1); + if (newend > previousend) { // we merge + previousrl->length = (uint16_t)(newend - 1 - previousrl->value); + run->runs[run->n_runs - 1] = *previousrl; + } + } +} + +/** + * Like run_container_append but it is assumed that the content of run is empty. + */ +static inline rle16_t run_container_append_first(run_container_t *run, + rle16_t vl) { + run->runs[run->n_runs] = vl; + run->n_runs++; + return vl; +} + +/** + * append a single value given by val to the run container, possibly merging. + * It is assumed that the value would be inserted at the end of the container, + * no check is made. + * It is assumed that the run container has the necessary capacity: caller is + * responsible for checking memory capacity. + * + * This is not a safe function, it is meant for performance: use with care. + */ +static inline void run_container_append_value(run_container_t *run, + uint16_t val, + rle16_t *previousrl) { + const uint32_t previousend = previousrl->value + previousrl->length; + if (val > previousend + 1) { // we add a new one + //*previousrl = (rle16_t){.value = val, .length = 0};// requires C99 + previousrl->value = val; + previousrl->length = 0; + + run->runs[run->n_runs] = *previousrl; + run->n_runs++; + } else if (val == previousend + 1) { // we merge + previousrl->length++; + run->runs[run->n_runs - 1] = *previousrl; + } +} + +/** + * Like run_container_append_value but it is assumed that the content of run is + * empty. + */ +static inline rle16_t run_container_append_value_first(run_container_t *run, + uint16_t val) { + // rle16_t newrle = (rle16_t){.value = val, .length = 0};// requires C99 + rle16_t newrle; + newrle.value = val; + newrle.length = 0; + + run->runs[run->n_runs] = newrle; + run->n_runs++; + return newrle; +} + +/* Check whether the container spans the whole chunk (cardinality = 1<<16). + * This check can be done in constant time (inexpensive). */ +static inline bool run_container_is_full(const run_container_t *run) { + rle16_t vl = run->runs[0]; + return (run->n_runs == 1) && (vl.value == 0) && (vl.length == 0xFFFF); +} + +/* Compute the union of `src_1' and `src_2' and write the result to `dst' + * It is assumed that `dst' is distinct from both `src_1' and `src_2'. */ +void run_container_union(const run_container_t *src_1, + const run_container_t *src_2, run_container_t *dst); + +/* Compute the union of `src_1' and `src_2' and write the result to `src_1' */ +void run_container_union_inplace(run_container_t *src_1, + const run_container_t *src_2); + +/* Compute the intersection of src_1 and src_2 and write the result to + * dst. It is assumed that dst is distinct from both src_1 and src_2. */ +void run_container_intersection(const run_container_t *src_1, + const run_container_t *src_2, + run_container_t *dst); + +/* Compute the size of the intersection of src_1 and src_2 . */ +int run_container_intersection_cardinality(const run_container_t *src_1, + const run_container_t *src_2); + +/* Check whether src_1 and src_2 intersect. */ +bool run_container_intersect(const run_container_t *src_1, + const run_container_t *src_2); + +/* Compute the symmetric difference of `src_1' and `src_2' and write the result + * to `dst' + * It is assumed that `dst' is distinct from both `src_1' and `src_2'. */ +void run_container_xor(const run_container_t *src_1, + const run_container_t *src_2, run_container_t *dst); + +/* + * Write out the 16-bit integers contained in this container as a list of 32-bit + * integers using base + * as the starting value (it might be expected that base has zeros in its 16 + * least significant bits). + * The function returns the number of values written. + * The caller is responsible for allocating enough memory in out. + */ +int run_container_to_uint32_array(void *vout, const run_container_t *cont, + uint32_t base); + +/* + * Print this container using printf (useful for debugging). + */ +void run_container_printf(const run_container_t *v); + +/* + * Print this container using printf as a comma-separated list of 32-bit + * integers starting at base. + */ +void run_container_printf_as_uint32_array(const run_container_t *v, + uint32_t base); + +/** + * Return the serialized size in bytes of a container having "num_runs" runs. + */ +static inline int32_t run_container_serialized_size_in_bytes(int32_t num_runs) { + return sizeof(uint16_t) + + sizeof(rle16_t) * num_runs; // each run requires 2 2-byte entries. +} + +bool run_container_iterate(const run_container_t *cont, uint32_t base, + roaring_iterator iterator, void *ptr); +bool run_container_iterate64(const run_container_t *cont, uint32_t base, + roaring_iterator64 iterator, uint64_t high_bits, + void *ptr); + +/** + * Writes the underlying array to buf, outputs how many bytes were written. + * This is meant to be byte-by-byte compatible with the Java and Go versions of + * Roaring. + * The number of bytes written should be run_container_size_in_bytes(container). + */ +int32_t run_container_write(const run_container_t *container, char *buf); + +/** + * Reads the instance from buf, outputs how many bytes were read. + * This is meant to be byte-by-byte compatible with the Java and Go versions of + * Roaring. + * The number of bytes read should be bitset_container_size_in_bytes(container). + * The cardinality parameter is provided for consistency with other containers, + * but + * it might be effectively ignored.. + */ +int32_t run_container_read(int32_t cardinality, run_container_t *container, + const char *buf); + +/** + * Return the serialized size in bytes of a container (see run_container_write). + * This is meant to be compatible with the Java and Go versions of Roaring. + */ +static inline int32_t run_container_size_in_bytes( + const run_container_t *container) { + return run_container_serialized_size_in_bytes(container->n_runs); +} + +/** + * Return true if the two containers have the same content. + */ +static inline bool run_container_equals(const run_container_t *container1, + const run_container_t *container2) { + if (container1->n_runs != container2->n_runs) { + return false; + } + return memequals(container1->runs, container2->runs, + container1->n_runs * sizeof(rle16_t)); +} + +/** +* Return true if container1 is a subset of container2. +*/ +bool run_container_is_subset(const run_container_t *container1, + const run_container_t *container2); + +/** + * Used in a start-finish scan that appends segments, for XOR and NOT + */ + +void run_container_smart_append_exclusive(run_container_t *src, + const uint16_t start, + const uint16_t length); + +/** +* The new container consists of a single run [start,stop). +* It is required that stop>start, the caller is responsibility for this check. +* It is required that stop <= (1<<16), the caller is responsibility for this check. +* The cardinality of the created container is stop - start. +* Returns NULL on failure +*/ +static inline run_container_t *run_container_create_range(uint32_t start, + uint32_t stop) { + run_container_t *rc = run_container_create_given_capacity(1); + if (rc) { + rle16_t r; + r.value = (uint16_t)start; + r.length = (uint16_t)(stop - start - 1); + run_container_append_first(rc, r); + } + return rc; +} + +/** + * If the element of given rank is in this container, supposing that the first + * element has rank start_rank, then the function returns true and sets element + * accordingly. + * Otherwise, it returns false and update start_rank. + */ +bool run_container_select(const run_container_t *container, + uint32_t *start_rank, uint32_t rank, + uint32_t *element); + +/* Compute the difference of src_1 and src_2 and write the result to + * dst. It is assumed that dst is distinct from both src_1 and src_2. */ + +void run_container_andnot(const run_container_t *src_1, + const run_container_t *src_2, run_container_t *dst); + +/* Returns the smallest value (assumes not empty) */ +static inline uint16_t run_container_minimum(const run_container_t *run) { + if (run->n_runs == 0) return 0; + return run->runs[0].value; +} + +/* Returns the largest value (assumes not empty) */ +static inline uint16_t run_container_maximum(const run_container_t *run) { + if (run->n_runs == 0) return 0; + return run->runs[run->n_runs - 1].value + run->runs[run->n_runs - 1].length; +} + +/* Returns the number of values equal or smaller than x */ +int run_container_rank(const run_container_t *arr, uint16_t x); + +/* Returns the index of the first run containing a value at least as large as x, or -1 */ +static inline int run_container_index_equalorlarger(const run_container_t *arr, uint16_t x) { + int32_t index = interleavedBinarySearch(arr->runs, arr->n_runs, x); + if (index >= 0) return index; + index = -index - 2; // points to preceding run, possibly -1 + if (index != -1) { // possible match + int32_t offset = x - arr->runs[index].value; + int32_t le = arr->runs[index].length; + if (offset <= le) return index; + } + index += 1; + if(index < arr->n_runs) { + return index; + } + return -1; +} + +/* + * Add all values in range [min, max] using hint. + */ +static inline void run_container_add_range_nruns(run_container_t* run, + uint32_t min, uint32_t max, + int32_t nruns_less, + int32_t nruns_greater) { + int32_t nruns_common = run->n_runs - nruns_less - nruns_greater; + if (nruns_common == 0) { + makeRoomAtIndex(run, nruns_less); + run->runs[nruns_less].value = min; + run->runs[nruns_less].length = max - min; + } else { + uint32_t common_min = run->runs[nruns_less].value; + uint32_t common_max = run->runs[nruns_less + nruns_common - 1].value + + run->runs[nruns_less + nruns_common - 1].length; + uint32_t result_min = (common_min < min) ? common_min : min; + uint32_t result_max = (common_max > max) ? common_max : max; + + run->runs[nruns_less].value = result_min; + run->runs[nruns_less].length = result_max - result_min; + + memmove(&(run->runs[nruns_less + 1]), + &(run->runs[run->n_runs - nruns_greater]), + nruns_greater*sizeof(rle16_t)); + run->n_runs = nruns_less + 1 + nruns_greater; + } +} + +/** + * Add all values in range [min, max] + */ +static inline void run_container_add_range(run_container_t* run, + uint32_t min, uint32_t max) { + int32_t nruns_greater = rle16_count_greater(run->runs, run->n_runs, max); + int32_t nruns_less = rle16_count_less(run->runs, run->n_runs - nruns_greater, min); + run_container_add_range_nruns(run, min, max, nruns_less, nruns_greater); +} + +/** + * Shifts last $count elements either left (distance < 0) or right (distance > 0) + */ +static inline void run_container_shift_tail(run_container_t* run, + int32_t count, int32_t distance) { + if (distance > 0) { + if (run->capacity < count+distance) { + run_container_grow(run, count+distance, true); + } + } + int32_t srcpos = run->n_runs - count; + int32_t dstpos = srcpos + distance; + memmove(&(run->runs[dstpos]), &(run->runs[srcpos]), sizeof(rle16_t) * count); + run->n_runs += distance; +} + +/** + * Remove all elements in range [min, max] + */ +static inline void run_container_remove_range(run_container_t *run, uint32_t min, uint32_t max) { + int32_t first = rle16_find_run(run->runs, run->n_runs, min); + int32_t last = rle16_find_run(run->runs, run->n_runs, max); + + if (first >= 0 && min > run->runs[first].value && + max < ((uint32_t)run->runs[first].value + (uint32_t)run->runs[first].length)) { + // split this run into two adjacent runs + + // right subinterval + makeRoomAtIndex(run, first+1); + run->runs[first+1].value = max + 1; + run->runs[first+1].length = (run->runs[first].value + run->runs[first].length) - (max + 1); + + // left subinterval + run->runs[first].length = (min - 1) - run->runs[first].value; + + return; + } + + // update left-most partial run + if (first >= 0) { + if (min > run->runs[first].value) { + run->runs[first].length = (min - 1) - run->runs[first].value; + first++; + } + } else { + first = -first-1; + } + + // update right-most run + if (last >= 0) { + uint16_t run_max = run->runs[last].value + run->runs[last].length; + if (run_max > max) { + run->runs[last].value = max + 1; + run->runs[last].length = run_max - (max + 1); + last--; + } + } else { + last = (-last-1) - 1; + } + + // remove intermediate runs + if (first <= last) { + run_container_shift_tail(run, run->n_runs - (last+1), -(last-first+1)); + } +} + + +#endif /* INCLUDE_CONTAINERS_RUN_H_ */ +/* end file include/roaring/containers/run.h */ +/* begin file include/roaring/containers/convert.h */ +/* + * convert.h + * + */ + +#ifndef INCLUDE_CONTAINERS_CONVERT_H_ +#define INCLUDE_CONTAINERS_CONVERT_H_ + + +/* Convert an array into a bitset. The input container is not freed or modified. + */ +bitset_container_t *bitset_container_from_array(const array_container_t *arr); + +/* Convert a run into a bitset. The input container is not freed or modified. */ +bitset_container_t *bitset_container_from_run(const run_container_t *arr); + +/* Convert a run into an array. The input container is not freed or modified. */ +array_container_t *array_container_from_run(const run_container_t *arr); + +/* Convert a bitset into an array. The input container is not freed or modified. + */ +array_container_t *array_container_from_bitset(const bitset_container_t *bits); + +/* Convert an array into a run. The input container is not freed or modified. + */ +run_container_t *run_container_from_array(const array_container_t *c); + +/* convert a run into either an array or a bitset + * might free the container. This does not free the input run container. */ +void *convert_to_bitset_or_array_container(run_container_t *r, int32_t card, + uint8_t *resulttype); + +/* convert containers to and from runcontainers, as is most space efficient. + * The container might be freed. */ +void *convert_run_optimize(void *c, uint8_t typecode_original, + uint8_t *typecode_after); + +/* converts a run container to either an array or a bitset, IF it saves space. + */ +/* If a conversion occurs, the caller is responsible to free the original + * container and + * he becomes responsible to free the new one. */ +void *convert_run_to_efficient_container(run_container_t *c, + uint8_t *typecode_after); +// like convert_run_to_efficient_container but frees the old result if needed +void *convert_run_to_efficient_container_and_free(run_container_t *c, + uint8_t *typecode_after); + +/** + * Create new bitset container which is a union of run container and + * range [min, max]. Caller is responsible for freeing run container. + */ +bitset_container_t *bitset_container_from_run_range(const run_container_t *run, + uint32_t min, uint32_t max); + +#endif /* INCLUDE_CONTAINERS_CONVERT_H_ */ +/* end file include/roaring/containers/convert.h */ +/* begin file include/roaring/containers/mixed_equal.h */ +/* + * mixed_equal.h + * + */ + +#ifndef CONTAINERS_MIXED_EQUAL_H_ +#define CONTAINERS_MIXED_EQUAL_H_ + + +/** + * Return true if the two containers have the same content. + */ +bool array_container_equal_bitset(const array_container_t* container1, + const bitset_container_t* container2); + +/** + * Return true if the two containers have the same content. + */ +bool run_container_equals_array(const run_container_t* container1, + const array_container_t* container2); +/** + * Return true if the two containers have the same content. + */ +bool run_container_equals_bitset(const run_container_t* container1, + const bitset_container_t* container2); + +#endif /* CONTAINERS_MIXED_EQUAL_H_ */ +/* end file include/roaring/containers/mixed_equal.h */ +/* begin file include/roaring/containers/mixed_subset.h */ +/* + * mixed_subset.h + * + */ + +#ifndef CONTAINERS_MIXED_SUBSET_H_ +#define CONTAINERS_MIXED_SUBSET_H_ + + +/** + * Return true if container1 is a subset of container2. + */ +bool array_container_is_subset_bitset(const array_container_t* container1, + const bitset_container_t* container2); + +/** +* Return true if container1 is a subset of container2. + */ +bool run_container_is_subset_array(const run_container_t* container1, + const array_container_t* container2); + +/** +* Return true if container1 is a subset of container2. + */ +bool array_container_is_subset_run(const array_container_t* container1, + const run_container_t* container2); + +/** +* Return true if container1 is a subset of container2. + */ +bool run_container_is_subset_bitset(const run_container_t* container1, + const bitset_container_t* container2); + +/** +* Return true if container1 is a subset of container2. +*/ +bool bitset_container_is_subset_run(const bitset_container_t* container1, + const run_container_t* container2); + +#endif /* CONTAINERS_MIXED_SUBSET_H_ */ +/* end file include/roaring/containers/mixed_subset.h */ +/* begin file include/roaring/containers/mixed_andnot.h */ +/* + * mixed_andnot.h + */ +#ifndef INCLUDE_CONTAINERS_MIXED_ANDNOT_H_ +#define INCLUDE_CONTAINERS_MIXED_ANDNOT_H_ + + +/* Compute the andnot of src_1 and src_2 and write the result to + * dst, a valid array container that could be the same as dst.*/ +void array_bitset_container_andnot(const array_container_t *src_1, + const bitset_container_t *src_2, + array_container_t *dst); + +/* Compute the andnot of src_1 and src_2 and write the result to + * src_1 */ + +void array_bitset_container_iandnot(array_container_t *src_1, + const bitset_container_t *src_2); + +/* Compute the andnot of src_1 and src_2 and write the result to + * dst, which does not initially have a valid container. + * Return true for a bitset result; false for array + */ + +bool bitset_array_container_andnot(const bitset_container_t *src_1, + const array_container_t *src_2, void **dst); + +/* Compute the andnot of src_1 and src_2 and write the result to + * dst (which has no container initially). It will modify src_1 + * to be dst if the result is a bitset. Otherwise, it will + * free src_1 and dst will be a new array container. In both + * cases, the caller is responsible for deallocating dst. + * Returns true iff dst is a bitset */ + +bool bitset_array_container_iandnot(bitset_container_t *src_1, + const array_container_t *src_2, void **dst); + +/* Compute the andnot of src_1 and src_2 and write the result to + * dst. Result may be either a bitset or an array container + * (returns "result is bitset"). dst does not initially have + * any container, but becomes either a bitset container (return + * result true) or an array container. + */ + +bool run_bitset_container_andnot(const run_container_t *src_1, + const bitset_container_t *src_2, void **dst); + +/* Compute the andnot of src_1 and src_2 and write the result to + * dst. Result may be either a bitset or an array container + * (returns "result is bitset"). dst does not initially have + * any container, but becomes either a bitset container (return + * result true) or an array container. + */ + +bool run_bitset_container_iandnot(run_container_t *src_1, + const bitset_container_t *src_2, void **dst); + +/* Compute the andnot of src_1 and src_2 and write the result to + * dst. Result may be either a bitset or an array container + * (returns "result is bitset"). dst does not initially have + * any container, but becomes either a bitset container (return + * result true) or an array container. + */ + +bool bitset_run_container_andnot(const bitset_container_t *src_1, + const run_container_t *src_2, void **dst); + +/* Compute the andnot of src_1 and src_2 and write the result to + * dst (which has no container initially). It will modify src_1 + * to be dst if the result is a bitset. Otherwise, it will + * free src_1 and dst will be a new array container. In both + * cases, the caller is responsible for deallocating dst. + * Returns true iff dst is a bitset */ + +bool bitset_run_container_iandnot(bitset_container_t *src_1, + const run_container_t *src_2, void **dst); + +/* dst does not indicate a valid container initially. Eventually it + * can become any type of container. + */ + +int run_array_container_andnot(const run_container_t *src_1, + const array_container_t *src_2, void **dst); + +/* Compute the andnot of src_1 and src_2 and write the result to + * dst (which has no container initially). It will modify src_1 + * to be dst if the result is a bitset. Otherwise, it will + * free src_1 and dst will be a new array container. In both + * cases, the caller is responsible for deallocating dst. + * Returns true iff dst is a bitset */ + +int run_array_container_iandnot(run_container_t *src_1, + const array_container_t *src_2, void **dst); + +/* dst must be a valid array container, allowed to be src_1 */ + +void array_run_container_andnot(const array_container_t *src_1, + const run_container_t *src_2, + array_container_t *dst); + +/* dst does not indicate a valid container initially. Eventually it + * can become any kind of container. + */ + +void array_run_container_iandnot(array_container_t *src_1, + const run_container_t *src_2); + +/* dst does not indicate a valid container initially. Eventually it + * can become any kind of container. + */ + +int run_run_container_andnot(const run_container_t *src_1, + const run_container_t *src_2, void **dst); + +/* Compute the andnot of src_1 and src_2 and write the result to + * dst (which has no container initially). It will modify src_1 + * to be dst if the result is a bitset. Otherwise, it will + * free src_1 and dst will be a new array container. In both + * cases, the caller is responsible for deallocating dst. + * Returns true iff dst is a bitset */ + +int run_run_container_iandnot(run_container_t *src_1, + const run_container_t *src_2, void **dst); + +/* + * dst is a valid array container and may be the same as src_1 + */ + +void array_array_container_andnot(const array_container_t *src_1, + const array_container_t *src_2, + array_container_t *dst); + +/* inplace array-array andnot will always be able to reuse the space of + * src_1 */ +void array_array_container_iandnot(array_container_t *src_1, + const array_container_t *src_2); + +/* Compute the andnot of src_1 and src_2 and write the result to + * dst (which has no container initially). Return value is + * "dst is a bitset" + */ + +bool bitset_bitset_container_andnot(const bitset_container_t *src_1, + const bitset_container_t *src_2, + void **dst); + +/* Compute the andnot of src_1 and src_2 and write the result to + * dst (which has no container initially). It will modify src_1 + * to be dst if the result is a bitset. Otherwise, it will + * free src_1 and dst will be a new array container. In both + * cases, the caller is responsible for deallocating dst. + * Returns true iff dst is a bitset */ + +bool bitset_bitset_container_iandnot(bitset_container_t *src_1, + const bitset_container_t *src_2, + void **dst); +#endif +/* end file include/roaring/containers/mixed_andnot.h */ +/* begin file include/roaring/containers/mixed_intersection.h */ +/* + * mixed_intersection.h + * + */ + +#ifndef INCLUDE_CONTAINERS_MIXED_INTERSECTION_H_ +#define INCLUDE_CONTAINERS_MIXED_INTERSECTION_H_ + +/* These functions appear to exclude cases where the + * inputs have the same type and the output is guaranteed + * to have the same type as the inputs. Eg, array intersection + */ + + +/* Compute the intersection of src_1 and src_2 and write the result to + * dst. It is allowed for dst to be equal to src_1. We assume that dst is a + * valid container. */ +void array_bitset_container_intersection(const array_container_t *src_1, + const bitset_container_t *src_2, + array_container_t *dst); + +/* Compute the size of the intersection of src_1 and src_2. */ +int array_bitset_container_intersection_cardinality( + const array_container_t *src_1, const bitset_container_t *src_2); + + + +/* Checking whether src_1 and src_2 intersect. */ +bool array_bitset_container_intersect(const array_container_t *src_1, + const bitset_container_t *src_2); + +/* + * Compute the intersection between src_1 and src_2 and write the result + * to *dst. If the return function is true, the result is a bitset_container_t + * otherwise is a array_container_t. We assume that dst is not pre-allocated. In + * case of failure, *dst will be NULL. + */ +bool bitset_bitset_container_intersection(const bitset_container_t *src_1, + const bitset_container_t *src_2, + void **dst); + +/* Compute the intersection between src_1 and src_2 and write the result to + * dst. It is allowed for dst to be equal to src_1. We assume that dst is a + * valid container. */ +void array_run_container_intersection(const array_container_t *src_1, + const run_container_t *src_2, + array_container_t *dst); + +/* Compute the intersection between src_1 and src_2 and write the result to + * *dst. If the result is true then the result is a bitset_container_t + * otherwise is a array_container_t. + * If *dst == src_2, then an in-place intersection is attempted + **/ +bool run_bitset_container_intersection(const run_container_t *src_1, + const bitset_container_t *src_2, + void **dst); + +/* Compute the size of the intersection between src_1 and src_2 . */ +int array_run_container_intersection_cardinality(const array_container_t *src_1, + const run_container_t *src_2); + +/* Compute the size of the intersection between src_1 and src_2 + **/ +int run_bitset_container_intersection_cardinality(const run_container_t *src_1, + const bitset_container_t *src_2); + + +/* Check that src_1 and src_2 intersect. */ +bool array_run_container_intersect(const array_container_t *src_1, + const run_container_t *src_2); + +/* Check that src_1 and src_2 intersect. + **/ +bool run_bitset_container_intersect(const run_container_t *src_1, + const bitset_container_t *src_2); + +/* + * Same as bitset_bitset_container_intersection except that if the output is to + * be a + * bitset_container_t, then src_1 is modified and no allocation is made. + * If the output is to be an array_container_t, then caller is responsible + * to free the container. + * In all cases, the result is in *dst. + */ +bool bitset_bitset_container_intersection_inplace( + bitset_container_t *src_1, const bitset_container_t *src_2, void **dst); + +#endif /* INCLUDE_CONTAINERS_MIXED_INTERSECTION_H_ */ +/* end file include/roaring/containers/mixed_intersection.h */ +/* begin file include/roaring/containers/mixed_negation.h */ +/* + * mixed_negation.h + * + */ + +#ifndef INCLUDE_CONTAINERS_MIXED_NEGATION_H_ +#define INCLUDE_CONTAINERS_MIXED_NEGATION_H_ + + +/* Negation across the entire range of the container. + * Compute the negation of src and write the result + * to *dst. The complement of a + * sufficiently sparse set will always be dense and a hence a bitmap + * We assume that dst is pre-allocated and a valid bitset container + * There can be no in-place version. + */ +void array_container_negation(const array_container_t *src, + bitset_container_t *dst); + +/* Negation across the entire range of the container + * Compute the negation of src and write the result + * to *dst. A true return value indicates a bitset result, + * otherwise the result is an array container. + * We assume that dst is not pre-allocated. In + * case of failure, *dst will be NULL. + */ +bool bitset_container_negation(const bitset_container_t *src, void **dst); + +/* inplace version */ +/* + * Same as bitset_container_negation except that if the output is to + * be a + * bitset_container_t, then src is modified and no allocation is made. + * If the output is to be an array_container_t, then caller is responsible + * to free the container. + * In all cases, the result is in *dst. + */ +bool bitset_container_negation_inplace(bitset_container_t *src, void **dst); + +/* Negation across the entire range of container + * Compute the negation of src and write the result + * to *dst. + * Return values are the *_TYPECODES as defined * in containers.h + * We assume that dst is not pre-allocated. In + * case of failure, *dst will be NULL. + */ +int run_container_negation(const run_container_t *src, void **dst); + +/* + * Same as run_container_negation except that if the output is to + * be a + * run_container_t, and has the capacity to hold the result, + * then src is modified and no allocation is made. + * In all cases, the result is in *dst. + */ +int run_container_negation_inplace(run_container_t *src, void **dst); + +/* Negation across a range of the container. + * Compute the negation of src and write the result + * to *dst. Returns true if the result is a bitset container + * and false for an array container. *dst is not preallocated. + */ +bool array_container_negation_range(const array_container_t *src, + const int range_start, const int range_end, + void **dst); + +/* Even when the result would fit, it is unclear how to make an + * inplace version without inefficient copying. Thus this routine + * may be a wrapper for the non-in-place version + */ +bool array_container_negation_range_inplace(array_container_t *src, + const int range_start, + const int range_end, void **dst); + +/* Negation across a range of the container + * Compute the negation of src and write the result + * to *dst. A true return value indicates a bitset result, + * otherwise the result is an array container. + * We assume that dst is not pre-allocated. In + * case of failure, *dst will be NULL. + */ +bool bitset_container_negation_range(const bitset_container_t *src, + const int range_start, const int range_end, + void **dst); + +/* inplace version */ +/* + * Same as bitset_container_negation except that if the output is to + * be a + * bitset_container_t, then src is modified and no allocation is made. + * If the output is to be an array_container_t, then caller is responsible + * to free the container. + * In all cases, the result is in *dst. + */ +bool bitset_container_negation_range_inplace(bitset_container_t *src, + const int range_start, + const int range_end, void **dst); + +/* Negation across a range of container + * Compute the negation of src and write the result + * to *dst. Return values are the *_TYPECODES as defined * in containers.h + * We assume that dst is not pre-allocated. In + * case of failure, *dst will be NULL. + */ +int run_container_negation_range(const run_container_t *src, + const int range_start, const int range_end, + void **dst); + +/* + * Same as run_container_negation except that if the output is to + * be a + * run_container_t, and has the capacity to hold the result, + * then src is modified and no allocation is made. + * In all cases, the result is in *dst. + */ +int run_container_negation_range_inplace(run_container_t *src, + const int range_start, + const int range_end, void **dst); + +#endif /* INCLUDE_CONTAINERS_MIXED_NEGATION_H_ */ +/* end file include/roaring/containers/mixed_negation.h */ +/* begin file include/roaring/containers/mixed_union.h */ +/* + * mixed_intersection.h + * + */ + +#ifndef INCLUDE_CONTAINERS_MIXED_UNION_H_ +#define INCLUDE_CONTAINERS_MIXED_UNION_H_ + +/* These functions appear to exclude cases where the + * inputs have the same type and the output is guaranteed + * to have the same type as the inputs. Eg, bitset unions + */ + + +/* Compute the union of src_1 and src_2 and write the result to + * dst. It is allowed for src_2 to be dst. */ +void array_bitset_container_union(const array_container_t *src_1, + const bitset_container_t *src_2, + bitset_container_t *dst); + +/* Compute the union of src_1 and src_2 and write the result to + * dst. It is allowed for src_2 to be dst. This version does not + * update the cardinality of dst (it is set to BITSET_UNKNOWN_CARDINALITY). */ +void array_bitset_container_lazy_union(const array_container_t *src_1, + const bitset_container_t *src_2, + bitset_container_t *dst); + +/* + * Compute the union between src_1 and src_2 and write the result + * to *dst. If the return function is true, the result is a bitset_container_t + * otherwise is a array_container_t. We assume that dst is not pre-allocated. In + * case of failure, *dst will be NULL. + */ +bool array_array_container_union(const array_container_t *src_1, + const array_container_t *src_2, void **dst); + +/* + * Compute the union between src_1 and src_2 and write the result + * to *dst if it cannot be written to src_1. If the return function is true, + * the result is a bitset_container_t + * otherwise is a array_container_t. When the result is an array_container_t, it + * it either written to src_1 (if *dst is null) or to *dst. + * If the result is a bitset_container_t and *dst is null, then there was a failure. + */ +bool array_array_container_inplace_union(array_container_t *src_1, + const array_container_t *src_2, void **dst); + +/* + * Same as array_array_container_union except that it will more eagerly produce + * a bitset. + */ +bool array_array_container_lazy_union(const array_container_t *src_1, + const array_container_t *src_2, + void **dst); + +/* + * Same as array_array_container_inplace_union except that it will more eagerly produce + * a bitset. + */ +bool array_array_container_lazy_inplace_union(array_container_t *src_1, + const array_container_t *src_2, + void **dst); + +/* Compute the union of src_1 and src_2 and write the result to + * dst. We assume that dst is a + * valid container. The result might need to be further converted to array or + * bitset container, + * the caller is responsible for the eventual conversion. */ +void array_run_container_union(const array_container_t *src_1, + const run_container_t *src_2, + run_container_t *dst); + +/* Compute the union of src_1 and src_2 and write the result to + * src2. The result might need to be further converted to array or + * bitset container, + * the caller is responsible for the eventual conversion. */ +void array_run_container_inplace_union(const array_container_t *src_1, + run_container_t *src_2); + +/* Compute the union of src_1 and src_2 and write the result to + * dst. It is allowed for dst to be src_2. + * If run_container_is_full(src_1) is true, you must not be calling this + *function. + **/ +void run_bitset_container_union(const run_container_t *src_1, + const bitset_container_t *src_2, + bitset_container_t *dst); + +/* Compute the union of src_1 and src_2 and write the result to + * dst. It is allowed for dst to be src_2. This version does not + * update the cardinality of dst (it is set to BITSET_UNKNOWN_CARDINALITY). + * If run_container_is_full(src_1) is true, you must not be calling this + * function. + * */ +void run_bitset_container_lazy_union(const run_container_t *src_1, + const bitset_container_t *src_2, + bitset_container_t *dst); + +#endif /* INCLUDE_CONTAINERS_MIXED_UNION_H_ */ +/* end file include/roaring/containers/mixed_union.h */ +/* begin file include/roaring/containers/mixed_xor.h */ +/* + * mixed_xor.h + * + */ + +#ifndef INCLUDE_CONTAINERS_MIXED_XOR_H_ +#define INCLUDE_CONTAINERS_MIXED_XOR_H_ + +/* These functions appear to exclude cases where the + * inputs have the same type and the output is guaranteed + * to have the same type as the inputs. Eg, bitset unions + */ + +/* + * Java implementation (as of May 2016) for array_run, run_run + * and bitset_run don't do anything different for inplace. + * (They are not truly in place.) + */ + + + +/* Compute the xor of src_1 and src_2 and write the result to + * dst (which has no container initially). + * Result is true iff dst is a bitset */ +bool array_bitset_container_xor(const array_container_t *src_1, + const bitset_container_t *src_2, void **dst); + +/* Compute the xor of src_1 and src_2 and write the result to + * dst. It is allowed for src_2 to be dst. This version does not + * update the cardinality of dst (it is set to BITSET_UNKNOWN_CARDINALITY). + */ + +void array_bitset_container_lazy_xor(const array_container_t *src_1, + const bitset_container_t *src_2, + bitset_container_t *dst); +/* Compute the xor of src_1 and src_2 and write the result to + * dst (which has no container initially). Return value is + * "dst is a bitset" + */ + +bool bitset_bitset_container_xor(const bitset_container_t *src_1, + const bitset_container_t *src_2, void **dst); + +/* Compute the xor of src_1 and src_2 and write the result to + * dst. Result may be either a bitset or an array container + * (returns "result is bitset"). dst does not initially have + * any container, but becomes either a bitset container (return + * result true) or an array container. + */ + +bool run_bitset_container_xor(const run_container_t *src_1, + const bitset_container_t *src_2, void **dst); + +/* lazy xor. Dst is initialized and may be equal to src_2. + * Result is left as a bitset container, even if actual + * cardinality would dictate an array container. + */ + +void run_bitset_container_lazy_xor(const run_container_t *src_1, + const bitset_container_t *src_2, + bitset_container_t *dst); + +/* dst does not indicate a valid container initially. Eventually it + * can become any kind of container. + */ + +int array_run_container_xor(const array_container_t *src_1, + const run_container_t *src_2, void **dst); + +/* dst does not initially have a valid container. Creates either + * an array or a bitset container, indicated by return code + */ + +bool array_array_container_xor(const array_container_t *src_1, + const array_container_t *src_2, void **dst); + +/* dst does not initially have a valid container. Creates either + * an array or a bitset container, indicated by return code. + * A bitset container will not have a valid cardinality and the + * container type might not be correct for the actual cardinality + */ + +bool array_array_container_lazy_xor(const array_container_t *src_1, + const array_container_t *src_2, void **dst); + +/* Dst is a valid run container. (Can it be src_2? Let's say not.) + * Leaves result as run container, even if other options are + * smaller. + */ + +void array_run_container_lazy_xor(const array_container_t *src_1, + const run_container_t *src_2, + run_container_t *dst); + +/* dst does not indicate a valid container initially. Eventually it + * can become any kind of container. + */ + +int run_run_container_xor(const run_container_t *src_1, + const run_container_t *src_2, void **dst); + +/* INPLACE versions (initial implementation may not exploit all inplace + * opportunities (if any...) + */ + +/* Compute the xor of src_1 and src_2 and write the result to + * dst (which has no container initially). It will modify src_1 + * to be dst if the result is a bitset. Otherwise, it will + * free src_1 and dst will be a new array container. In both + * cases, the caller is responsible for deallocating dst. + * Returns true iff dst is a bitset */ + +bool bitset_array_container_ixor(bitset_container_t *src_1, + const array_container_t *src_2, void **dst); + +bool bitset_bitset_container_ixor(bitset_container_t *src_1, + const bitset_container_t *src_2, void **dst); + +bool array_bitset_container_ixor(array_container_t *src_1, + const bitset_container_t *src_2, void **dst); + +/* Compute the xor of src_1 and src_2 and write the result to + * dst. Result may be either a bitset or an array container + * (returns "result is bitset"). dst does not initially have + * any container, but becomes either a bitset container (return + * result true) or an array container. + */ + +bool run_bitset_container_ixor(run_container_t *src_1, + const bitset_container_t *src_2, void **dst); + +bool bitset_run_container_ixor(bitset_container_t *src_1, + const run_container_t *src_2, void **dst); + +/* dst does not indicate a valid container initially. Eventually it + * can become any kind of container. + */ + +int array_run_container_ixor(array_container_t *src_1, + const run_container_t *src_2, void **dst); + +int run_array_container_ixor(run_container_t *src_1, + const array_container_t *src_2, void **dst); + +bool array_array_container_ixor(array_container_t *src_1, + const array_container_t *src_2, void **dst); + +int run_run_container_ixor(run_container_t *src_1, const run_container_t *src_2, + void **dst); +#endif +/* end file include/roaring/containers/mixed_xor.h */ +/* begin file include/roaring/containers/containers.h */ +#ifndef CONTAINERS_CONTAINERS_H +#define CONTAINERS_CONTAINERS_H + +#include +#include +#include + + +// would enum be possible or better? + +/** + * The switch case statements follow + * BITSET_CONTAINER_TYPE_CODE -- ARRAY_CONTAINER_TYPE_CODE -- + * RUN_CONTAINER_TYPE_CODE + * so it makes more sense to number them 1, 2, 3 (in the vague hope that the + * compiler might exploit this ordering). + */ + +#define BITSET_CONTAINER_TYPE_CODE 1 +#define ARRAY_CONTAINER_TYPE_CODE 2 +#define RUN_CONTAINER_TYPE_CODE 3 +#define SHARED_CONTAINER_TYPE_CODE 4 + +// macro for pairing container type codes +#define CONTAINER_PAIR(c1, c2) (4 * (c1) + (c2)) + +/** + * A shared container is a wrapper around a container + * with reference counting. + */ + +struct shared_container_s { + void *container; + uint8_t typecode; + uint32_t counter; // to be managed atomically +}; + +typedef struct shared_container_s shared_container_t; + +/* + * With copy_on_write = true + * Create a new shared container if the typecode is not SHARED_CONTAINER_TYPE, + * otherwise, increase the count + * If copy_on_write = false, then clone. + * Return NULL in case of failure. + **/ +void *get_copy_of_container(void *container, uint8_t *typecode, + bool copy_on_write); + +/* Frees a shared container (actually decrement its counter and only frees when + * the counter falls to zero). */ +void shared_container_free(shared_container_t *container); + +/* extract a copy from the shared container, freeing the shared container if +there is just one instance left, +clone instances when the counter is higher than one +*/ +void *shared_container_extract_copy(shared_container_t *container, + uint8_t *typecode); + +/* access to container underneath */ +static inline const void *container_unwrap_shared( + const void *candidate_shared_container, uint8_t *type) { + if (*type == SHARED_CONTAINER_TYPE_CODE) { + *type = + ((const shared_container_t *)candidate_shared_container)->typecode; + assert(*type != SHARED_CONTAINER_TYPE_CODE); + return ((const shared_container_t *)candidate_shared_container)->container; + } else { + return candidate_shared_container; + } +} + + +/* access to container underneath */ +static inline void *container_mutable_unwrap_shared( + void *candidate_shared_container, uint8_t *type) { + if (*type == SHARED_CONTAINER_TYPE_CODE) { + *type = + ((shared_container_t *)candidate_shared_container)->typecode; + assert(*type != SHARED_CONTAINER_TYPE_CODE); + return ((shared_container_t *)candidate_shared_container)->container; + } else { + return candidate_shared_container; + } +} + +/* access to container underneath and queries its type */ +static inline uint8_t get_container_type(const void *container, uint8_t type) { + if (type == SHARED_CONTAINER_TYPE_CODE) { + return ((const shared_container_t *)container)->typecode; + } else { + return type; + } +} + +/** + * Copies a container, requires a typecode. This allocates new memory, caller + * is responsible for deallocation. If the container is not shared, then it is + * physically cloned. Shareable containers are not clonable. + */ +void *container_clone(const void *container, uint8_t typecode); + +/* access to container underneath, cloning it if needed */ +static inline void *get_writable_copy_if_shared( + void *candidate_shared_container, uint8_t *type) { + if (*type == SHARED_CONTAINER_TYPE_CODE) { + return shared_container_extract_copy( + (shared_container_t *)candidate_shared_container, type); + } else { + return candidate_shared_container; + } +} + +/** + * End of shared container code + */ + +static const char *container_names[] = {"bitset", "array", "run", "shared"}; +static const char *shared_container_names[] = { + "bitset (shared)", "array (shared)", "run (shared)"}; + +// no matter what the initial container was, convert it to a bitset +// if a new container is produced, caller responsible for freeing the previous +// one +// container should not be a shared container +static inline void *container_to_bitset(void *container, uint8_t typecode) { + bitset_container_t *result = NULL; + switch (typecode) { + case BITSET_CONTAINER_TYPE_CODE: + return container; // nothing to do + case ARRAY_CONTAINER_TYPE_CODE: + result = + bitset_container_from_array((array_container_t *)container); + return result; + case RUN_CONTAINER_TYPE_CODE: + result = bitset_container_from_run((run_container_t *)container); + return result; + case SHARED_CONTAINER_TYPE_CODE: + default: + assert(false); + __builtin_unreachable(); + return 0; // unreached + } +} + +/** + * Get the container name from the typecode + */ +static inline const char *get_container_name(uint8_t typecode) { + switch (typecode) { + case BITSET_CONTAINER_TYPE_CODE: + return container_names[0]; + case ARRAY_CONTAINER_TYPE_CODE: + return container_names[1]; + case RUN_CONTAINER_TYPE_CODE: + return container_names[2]; + case SHARED_CONTAINER_TYPE_CODE: + return container_names[3]; + default: + assert(false); + __builtin_unreachable(); + return "unknown"; + } +} + +static inline const char *get_full_container_name(const void *container, + uint8_t typecode) { + switch (typecode) { + case BITSET_CONTAINER_TYPE_CODE: + return container_names[0]; + case ARRAY_CONTAINER_TYPE_CODE: + return container_names[1]; + case RUN_CONTAINER_TYPE_CODE: + return container_names[2]; + case SHARED_CONTAINER_TYPE_CODE: + switch (((const shared_container_t *)container)->typecode) { + case BITSET_CONTAINER_TYPE_CODE: + return shared_container_names[0]; + case ARRAY_CONTAINER_TYPE_CODE: + return shared_container_names[1]; + case RUN_CONTAINER_TYPE_CODE: + return shared_container_names[2]; + default: + assert(false); + __builtin_unreachable(); + return "unknown"; + } + break; + default: + assert(false); + __builtin_unreachable(); + return "unknown"; + } + __builtin_unreachable(); + return NULL; +} + +/** + * Get the container cardinality (number of elements), requires a typecode + */ +static inline int container_get_cardinality(const void *container, + uint8_t typecode) { + container = container_unwrap_shared(container, &typecode); + switch (typecode) { + case BITSET_CONTAINER_TYPE_CODE: + return bitset_container_cardinality( + (const bitset_container_t *)container); + case ARRAY_CONTAINER_TYPE_CODE: + return array_container_cardinality( + (const array_container_t *)container); + case RUN_CONTAINER_TYPE_CODE: + return run_container_cardinality( + (const run_container_t *)container); + case SHARED_CONTAINER_TYPE_CODE: + default: + assert(false); + __builtin_unreachable(); + return 0; // unreached + } +} + + + +// returns true if a container is known to be full. Note that a lazy bitset +// container +// might be full without us knowing +static inline bool container_is_full(const void *container, uint8_t typecode) { + container = container_unwrap_shared(container, &typecode); + switch (typecode) { + case BITSET_CONTAINER_TYPE_CODE: + return bitset_container_cardinality( + (const bitset_container_t *)container) == (1 << 16); + case ARRAY_CONTAINER_TYPE_CODE: + return array_container_cardinality( + (const array_container_t *)container) == (1 << 16); + case RUN_CONTAINER_TYPE_CODE: + return run_container_is_full((const run_container_t *)container); + case SHARED_CONTAINER_TYPE_CODE: + default: + assert(false); + __builtin_unreachable(); + return 0; // unreached + } +} + +static inline int container_shrink_to_fit(void *container, uint8_t typecode) { + container = container_mutable_unwrap_shared(container, &typecode); + switch (typecode) { + case BITSET_CONTAINER_TYPE_CODE: + return 0; // no shrinking possible + case ARRAY_CONTAINER_TYPE_CODE: + return array_container_shrink_to_fit( + (array_container_t *)container); + case RUN_CONTAINER_TYPE_CODE: + return run_container_shrink_to_fit((run_container_t *)container); + case SHARED_CONTAINER_TYPE_CODE: + default: + assert(false); + __builtin_unreachable(); + return 0; // unreached + } +} + + +/** + * make a container with a run of ones + */ +/* initially always use a run container, even if an array might be + * marginally + * smaller */ +static inline void *container_range_of_ones(uint32_t range_start, + uint32_t range_end, + uint8_t *result_type) { + assert(range_end >= range_start); + uint64_t cardinality = range_end - range_start + 1; + if(cardinality <= 2) { + *result_type = ARRAY_CONTAINER_TYPE_CODE; + return array_container_create_range(range_start, range_end); + } else { + *result_type = RUN_CONTAINER_TYPE_CODE; + return run_container_create_range(range_start, range_end); + } +} + + +/* Create a container with all the values between in [min,max) at a + distance k*step from min. */ +static inline void *container_from_range(uint8_t *type, uint32_t min, + uint32_t max, uint16_t step) { + if (step == 0) return NULL; // being paranoid + if (step == 1) { + return container_range_of_ones(min,max,type); + // Note: the result is not always a run (need to check the cardinality) + //*type = RUN_CONTAINER_TYPE_CODE; + //return run_container_create_range(min, max); + } + int size = (max - min + step - 1) / step; + if (size <= DEFAULT_MAX_SIZE) { // array container + *type = ARRAY_CONTAINER_TYPE_CODE; + array_container_t *array = array_container_create_given_capacity(size); + array_container_add_from_range(array, min, max, step); + assert(array->cardinality == size); + return array; + } else { // bitset container + *type = BITSET_CONTAINER_TYPE_CODE; + bitset_container_t *bitset = bitset_container_create(); + bitset_container_add_from_range(bitset, min, max, step); + assert(bitset->cardinality == size); + return bitset; + } +} + +/** + * "repair" the container after lazy operations. + */ +static inline void *container_repair_after_lazy(void *container, + uint8_t *typecode) { + container = get_writable_copy_if_shared( + container, typecode); // TODO: this introduces unnecessary cloning + void *result = NULL; + switch (*typecode) { + case BITSET_CONTAINER_TYPE_CODE: + ((bitset_container_t *)container)->cardinality = + bitset_container_compute_cardinality( + (bitset_container_t *)container); + if (((bitset_container_t *)container)->cardinality <= + DEFAULT_MAX_SIZE) { + result = array_container_from_bitset( + (const bitset_container_t *)container); + bitset_container_free((bitset_container_t *)container); + *typecode = ARRAY_CONTAINER_TYPE_CODE; + return result; + } + return container; + case ARRAY_CONTAINER_TYPE_CODE: + return container; // nothing to do + case RUN_CONTAINER_TYPE_CODE: + return convert_run_to_efficient_container_and_free( + (run_container_t *)container, typecode); + case SHARED_CONTAINER_TYPE_CODE: + default: + assert(false); + __builtin_unreachable(); + return 0; // unreached + } +} + +/** + * Writes the underlying array to buf, outputs how many bytes were written. + * This is meant to be byte-by-byte compatible with the Java and Go versions of + * Roaring. + * The number of bytes written should be + * container_write(container, buf). + * + */ +static inline int32_t container_write(const void *container, uint8_t typecode, + char *buf) { + container = container_unwrap_shared(container, &typecode); + switch (typecode) { + case BITSET_CONTAINER_TYPE_CODE: + return bitset_container_write((const bitset_container_t *)container, buf); + case ARRAY_CONTAINER_TYPE_CODE: + return array_container_write((const array_container_t *)container, buf); + case RUN_CONTAINER_TYPE_CODE: + return run_container_write((const run_container_t *)container, buf); + case SHARED_CONTAINER_TYPE_CODE: + default: + assert(false); + __builtin_unreachable(); + return 0; // unreached + } +} + +/** + * Get the container size in bytes under portable serialization (see + * container_write), requires a + * typecode + */ +static inline int32_t container_size_in_bytes(const void *container, + uint8_t typecode) { + container = container_unwrap_shared(container, &typecode); + switch (typecode) { + case BITSET_CONTAINER_TYPE_CODE: + return bitset_container_size_in_bytes( + (const bitset_container_t *)container); + case ARRAY_CONTAINER_TYPE_CODE: + return array_container_size_in_bytes( + (const array_container_t *)container); + case RUN_CONTAINER_TYPE_CODE: + return run_container_size_in_bytes((const run_container_t *)container); + case SHARED_CONTAINER_TYPE_CODE: + default: + assert(false); + __builtin_unreachable(); + return 0; // unreached + } +} + +/** + * print the container (useful for debugging), requires a typecode + */ +void container_printf(const void *container, uint8_t typecode); + +/** + * print the content of the container as a comma-separated list of 32-bit values + * starting at base, requires a typecode + */ +void container_printf_as_uint32_array(const void *container, uint8_t typecode, + uint32_t base); + +/** + * Checks whether a container is not empty, requires a typecode + */ +static inline bool container_nonzero_cardinality(const void *container, + uint8_t typecode) { + container = container_unwrap_shared(container, &typecode); + switch (typecode) { + case BITSET_CONTAINER_TYPE_CODE: + return bitset_container_const_nonzero_cardinality( + (const bitset_container_t *)container); + case ARRAY_CONTAINER_TYPE_CODE: + return array_container_nonzero_cardinality( + (const array_container_t *)container); + case RUN_CONTAINER_TYPE_CODE: + return run_container_nonzero_cardinality( + (const run_container_t *)container); + case SHARED_CONTAINER_TYPE_CODE: + default: + assert(false); + __builtin_unreachable(); + return 0; // unreached + } +} + +/** + * Recover memory from a container, requires a typecode + */ +void container_free(void *container, uint8_t typecode); + +/** + * Convert a container to an array of values, requires a typecode as well as a + * "base" (most significant values) + * Returns number of ints added. + */ +static inline int container_to_uint32_array(uint32_t *output, + const void *container, + uint8_t typecode, uint32_t base) { + container = container_unwrap_shared(container, &typecode); + switch (typecode) { + case BITSET_CONTAINER_TYPE_CODE: + return bitset_container_to_uint32_array( + output, (const bitset_container_t *)container, base); + case ARRAY_CONTAINER_TYPE_CODE: + return array_container_to_uint32_array( + output, (const array_container_t *)container, base); + case RUN_CONTAINER_TYPE_CODE: + return run_container_to_uint32_array( + output, (const run_container_t *)container, base); + case SHARED_CONTAINER_TYPE_CODE: + default: + assert(false); + __builtin_unreachable(); + return 0; // unreached + } +} + +/** + * Add a value to a container, requires a typecode, fills in new_typecode and + * return (possibly different) container. + * This function may allocate a new container, and caller is responsible for + * memory deallocation + */ +static inline void *container_add(void *container, uint16_t val, + uint8_t typecode, uint8_t *new_typecode) { + container = get_writable_copy_if_shared(container, &typecode); + switch (typecode) { + case BITSET_CONTAINER_TYPE_CODE: + bitset_container_set((bitset_container_t *)container, val); + *new_typecode = BITSET_CONTAINER_TYPE_CODE; + return container; + case ARRAY_CONTAINER_TYPE_CODE: { + array_container_t *ac = (array_container_t *)container; + if (array_container_try_add(ac, val, DEFAULT_MAX_SIZE) != -1) { + *new_typecode = ARRAY_CONTAINER_TYPE_CODE; + return ac; + } else { + bitset_container_t* bitset = bitset_container_from_array(ac); + bitset_container_add(bitset, val); + *new_typecode = BITSET_CONTAINER_TYPE_CODE; + return bitset; + } + } break; + case RUN_CONTAINER_TYPE_CODE: + // per Java, no container type adjustments are done (revisit?) + run_container_add((run_container_t *)container, val); + *new_typecode = RUN_CONTAINER_TYPE_CODE; + return container; + case SHARED_CONTAINER_TYPE_CODE: + default: + assert(false); + __builtin_unreachable(); + return NULL; + } +} + +/** + * Remove a value from a container, requires a typecode, fills in new_typecode + * and + * return (possibly different) container. + * This function may allocate a new container, and caller is responsible for + * memory deallocation + */ +static inline void *container_remove(void *container, uint16_t val, + uint8_t typecode, uint8_t *new_typecode) { + container = get_writable_copy_if_shared(container, &typecode); + switch (typecode) { + case BITSET_CONTAINER_TYPE_CODE: + if (bitset_container_remove((bitset_container_t *)container, val)) { + if (bitset_container_cardinality( + (bitset_container_t *)container) <= DEFAULT_MAX_SIZE) { + *new_typecode = ARRAY_CONTAINER_TYPE_CODE; + return array_container_from_bitset( + (bitset_container_t *)container); + } + } + *new_typecode = typecode; + return container; + case ARRAY_CONTAINER_TYPE_CODE: + *new_typecode = typecode; + array_container_remove((array_container_t *)container, val); + return container; + case RUN_CONTAINER_TYPE_CODE: + // per Java, no container type adjustments are done (revisit?) + run_container_remove((run_container_t *)container, val); + *new_typecode = RUN_CONTAINER_TYPE_CODE; + return container; + case SHARED_CONTAINER_TYPE_CODE: + default: + assert(false); + __builtin_unreachable(); + return NULL; + } +} + +/** + * Check whether a value is in a container, requires a typecode + */ +static inline bool container_contains(const void *container, uint16_t val, + uint8_t typecode) { + container = container_unwrap_shared(container, &typecode); + switch (typecode) { + case BITSET_CONTAINER_TYPE_CODE: + return bitset_container_get((const bitset_container_t *)container, + val); + case ARRAY_CONTAINER_TYPE_CODE: + return array_container_contains( + (const array_container_t *)container, val); + case RUN_CONTAINER_TYPE_CODE: + return run_container_contains((const run_container_t *)container, + val); + case SHARED_CONTAINER_TYPE_CODE: + default: + assert(false); + __builtin_unreachable(); + return false; + } +} + +/** + * Check whether a range of values from range_start (included) to range_end (excluded) + * is in a container, requires a typecode + */ +static inline bool container_contains_range(const void *container, uint32_t range_start, + uint32_t range_end, uint8_t typecode) { + container = container_unwrap_shared(container, &typecode); + switch (typecode) { + case BITSET_CONTAINER_TYPE_CODE: + return bitset_container_get_range((const bitset_container_t *)container, + range_start, range_end); + case ARRAY_CONTAINER_TYPE_CODE: + return array_container_contains_range((const array_container_t *)container, + range_start, range_end); + case RUN_CONTAINER_TYPE_CODE: + return run_container_contains_range((const run_container_t *)container, + range_start, range_end); + case SHARED_CONTAINER_TYPE_CODE: + default: + assert(false); + __builtin_unreachable(); + return false; + } +} + +int32_t container_serialize(const void *container, uint8_t typecode, + char *buf) WARN_UNUSED; + +uint32_t container_serialization_len(const void *container, uint8_t typecode); + +void *container_deserialize(uint8_t typecode, const char *buf, size_t buf_len); + +/** + * Returns true if the two containers have the same content. Note that + * two containers having different types can be "equal" in this sense. + */ +static inline bool container_equals(const void *c1, uint8_t type1, + const void *c2, uint8_t type2) { + c1 = container_unwrap_shared(c1, &type1); + c2 = container_unwrap_shared(c2, &type2); + switch (CONTAINER_PAIR(type1, type2)) { + case CONTAINER_PAIR(BITSET_CONTAINER_TYPE_CODE, + BITSET_CONTAINER_TYPE_CODE): + return bitset_container_equals((const bitset_container_t *)c1, + (const bitset_container_t *)c2); + case CONTAINER_PAIR(BITSET_CONTAINER_TYPE_CODE, + RUN_CONTAINER_TYPE_CODE): + return run_container_equals_bitset((const run_container_t *)c2, + (const bitset_container_t *)c1); + case CONTAINER_PAIR(RUN_CONTAINER_TYPE_CODE, + BITSET_CONTAINER_TYPE_CODE): + return run_container_equals_bitset((const run_container_t *)c1, + (const bitset_container_t *)c2); + case CONTAINER_PAIR(BITSET_CONTAINER_TYPE_CODE, + ARRAY_CONTAINER_TYPE_CODE): + // java would always return false? + return array_container_equal_bitset((const array_container_t *)c2, + (const bitset_container_t *)c1); + case CONTAINER_PAIR(ARRAY_CONTAINER_TYPE_CODE, + BITSET_CONTAINER_TYPE_CODE): + // java would always return false? + return array_container_equal_bitset((const array_container_t *)c1, + (const bitset_container_t *)c2); + case CONTAINER_PAIR(ARRAY_CONTAINER_TYPE_CODE, RUN_CONTAINER_TYPE_CODE): + return run_container_equals_array((const run_container_t *)c2, + (const array_container_t *)c1); + case CONTAINER_PAIR(RUN_CONTAINER_TYPE_CODE, ARRAY_CONTAINER_TYPE_CODE): + return run_container_equals_array((const run_container_t *)c1, + (const array_container_t *)c2); + case CONTAINER_PAIR(ARRAY_CONTAINER_TYPE_CODE, + ARRAY_CONTAINER_TYPE_CODE): + return array_container_equals((const array_container_t *)c1, + (const array_container_t *)c2); + case CONTAINER_PAIR(RUN_CONTAINER_TYPE_CODE, RUN_CONTAINER_TYPE_CODE): + return run_container_equals((const run_container_t *)c1, + (const run_container_t *)c2); + default: + assert(false); + __builtin_unreachable(); + return false; + } +} + +/** + * Returns true if the container c1 is a subset of the container c2. Note that + * c1 can be a subset of c2 even if they have a different type. + */ +static inline bool container_is_subset(const void *c1, uint8_t type1, + const void *c2, uint8_t type2) { + c1 = container_unwrap_shared(c1, &type1); + c2 = container_unwrap_shared(c2, &type2); + switch (CONTAINER_PAIR(type1, type2)) { + case CONTAINER_PAIR(BITSET_CONTAINER_TYPE_CODE, + BITSET_CONTAINER_TYPE_CODE): + return bitset_container_is_subset((const bitset_container_t *)c1, + (const bitset_container_t *)c2); + case CONTAINER_PAIR(BITSET_CONTAINER_TYPE_CODE, + RUN_CONTAINER_TYPE_CODE): + return bitset_container_is_subset_run((const bitset_container_t *)c1, + (const run_container_t *)c2); + case CONTAINER_PAIR(RUN_CONTAINER_TYPE_CODE, + BITSET_CONTAINER_TYPE_CODE): + return run_container_is_subset_bitset((const run_container_t *)c1, + (const bitset_container_t *)c2); + case CONTAINER_PAIR(BITSET_CONTAINER_TYPE_CODE, + ARRAY_CONTAINER_TYPE_CODE): + return false; // by construction, size(c1) > size(c2) + case CONTAINER_PAIR(ARRAY_CONTAINER_TYPE_CODE, + BITSET_CONTAINER_TYPE_CODE): + return array_container_is_subset_bitset((const array_container_t *)c1, + (const bitset_container_t *)c2); + case CONTAINER_PAIR(ARRAY_CONTAINER_TYPE_CODE, RUN_CONTAINER_TYPE_CODE): + return array_container_is_subset_run((const array_container_t *)c1, + (const run_container_t *)c2); + case CONTAINER_PAIR(RUN_CONTAINER_TYPE_CODE, ARRAY_CONTAINER_TYPE_CODE): + return run_container_is_subset_array((const run_container_t *)c1, + (const array_container_t *)c2); + case CONTAINER_PAIR(ARRAY_CONTAINER_TYPE_CODE, + ARRAY_CONTAINER_TYPE_CODE): + return array_container_is_subset((const array_container_t *)c1, + (const array_container_t *)c2); + case CONTAINER_PAIR(RUN_CONTAINER_TYPE_CODE, RUN_CONTAINER_TYPE_CODE): + return run_container_is_subset((const run_container_t *)c1, + (const run_container_t *)c2); + default: + assert(false); + __builtin_unreachable(); + return false; + } +} + +// macro-izations possibilities for generic non-inplace binary-op dispatch + +/** + * Compute intersection between two containers, generate a new container (having + * type result_type), requires a typecode. This allocates new memory, caller + * is responsible for deallocation. + */ +static inline void *container_and(const void *c1, uint8_t type1, const void *c2, + uint8_t type2, uint8_t *result_type) { + c1 = container_unwrap_shared(c1, &type1); + c2 = container_unwrap_shared(c2, &type2); + void *result = NULL; + switch (CONTAINER_PAIR(type1, type2)) { + case CONTAINER_PAIR(BITSET_CONTAINER_TYPE_CODE, + BITSET_CONTAINER_TYPE_CODE): + *result_type = bitset_bitset_container_intersection( + (const bitset_container_t *)c1, + (const bitset_container_t *)c2, &result) + ? BITSET_CONTAINER_TYPE_CODE + : ARRAY_CONTAINER_TYPE_CODE; + return result; + case CONTAINER_PAIR(ARRAY_CONTAINER_TYPE_CODE, + ARRAY_CONTAINER_TYPE_CODE): + result = array_container_create(); + array_container_intersection((const array_container_t *)c1, + (const array_container_t *)c2, + (array_container_t *)result); + *result_type = ARRAY_CONTAINER_TYPE_CODE; // never bitset + return result; + case CONTAINER_PAIR(RUN_CONTAINER_TYPE_CODE, RUN_CONTAINER_TYPE_CODE): + result = run_container_create(); + run_container_intersection((const run_container_t *)c1, + (const run_container_t *)c2, + (run_container_t *)result); + return convert_run_to_efficient_container_and_free( + (run_container_t *)result, result_type); + case CONTAINER_PAIR(BITSET_CONTAINER_TYPE_CODE, + ARRAY_CONTAINER_TYPE_CODE): + result = array_container_create(); + array_bitset_container_intersection((const array_container_t *)c2, + (const bitset_container_t *)c1, + (array_container_t *)result); + *result_type = ARRAY_CONTAINER_TYPE_CODE; // never bitset + return result; + case CONTAINER_PAIR(ARRAY_CONTAINER_TYPE_CODE, + BITSET_CONTAINER_TYPE_CODE): + result = array_container_create(); + *result_type = ARRAY_CONTAINER_TYPE_CODE; // never bitset + array_bitset_container_intersection((const array_container_t *)c1, + (const bitset_container_t *)c2, + (array_container_t *)result); + return result; + + case CONTAINER_PAIR(BITSET_CONTAINER_TYPE_CODE, + RUN_CONTAINER_TYPE_CODE): + *result_type = run_bitset_container_intersection( + (const run_container_t *)c2, + (const bitset_container_t *)c1, &result) + ? BITSET_CONTAINER_TYPE_CODE + : ARRAY_CONTAINER_TYPE_CODE; + return result; + case CONTAINER_PAIR(RUN_CONTAINER_TYPE_CODE, + BITSET_CONTAINER_TYPE_CODE): + *result_type = run_bitset_container_intersection( + (const run_container_t *)c1, + (const bitset_container_t *)c2, &result) + ? BITSET_CONTAINER_TYPE_CODE + : ARRAY_CONTAINER_TYPE_CODE; + return result; + case CONTAINER_PAIR(ARRAY_CONTAINER_TYPE_CODE, RUN_CONTAINER_TYPE_CODE): + result = array_container_create(); + *result_type = ARRAY_CONTAINER_TYPE_CODE; // never bitset + array_run_container_intersection((const array_container_t *)c1, + (const run_container_t *)c2, + (array_container_t *)result); + return result; + + case CONTAINER_PAIR(RUN_CONTAINER_TYPE_CODE, ARRAY_CONTAINER_TYPE_CODE): + result = array_container_create(); + *result_type = ARRAY_CONTAINER_TYPE_CODE; // never bitset + array_run_container_intersection((const array_container_t *)c2, + (const run_container_t *)c1, + (array_container_t *)result); + return result; + default: + assert(false); + __builtin_unreachable(); + return NULL; + } +} + +/** + * Compute the size of the intersection between two containers. + */ +static inline int container_and_cardinality(const void *c1, uint8_t type1, + const void *c2, uint8_t type2) { + c1 = container_unwrap_shared(c1, &type1); + c2 = container_unwrap_shared(c2, &type2); + switch (CONTAINER_PAIR(type1, type2)) { + case CONTAINER_PAIR(BITSET_CONTAINER_TYPE_CODE, + BITSET_CONTAINER_TYPE_CODE): + return bitset_container_and_justcard( + (const bitset_container_t *)c1, (const bitset_container_t *)c2); + case CONTAINER_PAIR(ARRAY_CONTAINER_TYPE_CODE, + ARRAY_CONTAINER_TYPE_CODE): + return array_container_intersection_cardinality( + (const array_container_t *)c1, (const array_container_t *)c2); + case CONTAINER_PAIR(RUN_CONTAINER_TYPE_CODE, RUN_CONTAINER_TYPE_CODE): + return run_container_intersection_cardinality( + (const run_container_t *)c1, (const run_container_t *)c2); + case CONTAINER_PAIR(BITSET_CONTAINER_TYPE_CODE, + ARRAY_CONTAINER_TYPE_CODE): + return array_bitset_container_intersection_cardinality( + (const array_container_t *)c2, (const bitset_container_t *)c1); + case CONTAINER_PAIR(ARRAY_CONTAINER_TYPE_CODE, + BITSET_CONTAINER_TYPE_CODE): + return array_bitset_container_intersection_cardinality( + (const array_container_t *)c1, (const bitset_container_t *)c2); + case CONTAINER_PAIR(BITSET_CONTAINER_TYPE_CODE, + RUN_CONTAINER_TYPE_CODE): + return run_bitset_container_intersection_cardinality( + (const run_container_t *)c2, (const bitset_container_t *)c1); + case CONTAINER_PAIR(RUN_CONTAINER_TYPE_CODE, + BITSET_CONTAINER_TYPE_CODE): + return run_bitset_container_intersection_cardinality( + (const run_container_t *)c1, (const bitset_container_t *)c2); + case CONTAINER_PAIR(ARRAY_CONTAINER_TYPE_CODE, RUN_CONTAINER_TYPE_CODE): + return array_run_container_intersection_cardinality( + (const array_container_t *)c1, (const run_container_t *)c2); + case CONTAINER_PAIR(RUN_CONTAINER_TYPE_CODE, ARRAY_CONTAINER_TYPE_CODE): + return array_run_container_intersection_cardinality( + (const array_container_t *)c2, (const run_container_t *)c1); + default: + assert(false); + __builtin_unreachable(); + return 0; + } +} + +/** + * Check whether two containers intersect. + */ +static inline bool container_intersect(const void *c1, uint8_t type1, const void *c2, + uint8_t type2) { + c1 = container_unwrap_shared(c1, &type1); + c2 = container_unwrap_shared(c2, &type2); + switch (CONTAINER_PAIR(type1, type2)) { + case CONTAINER_PAIR(BITSET_CONTAINER_TYPE_CODE, + BITSET_CONTAINER_TYPE_CODE): + return bitset_container_intersect( + (const bitset_container_t *)c1, + (const bitset_container_t *)c2); + case CONTAINER_PAIR(ARRAY_CONTAINER_TYPE_CODE, + ARRAY_CONTAINER_TYPE_CODE): + return array_container_intersect((const array_container_t *)c1, + (const array_container_t *)c2); + case CONTAINER_PAIR(RUN_CONTAINER_TYPE_CODE, RUN_CONTAINER_TYPE_CODE): + return run_container_intersect((const run_container_t *)c1, + (const run_container_t *)c2); + case CONTAINER_PAIR(BITSET_CONTAINER_TYPE_CODE, + ARRAY_CONTAINER_TYPE_CODE): + return array_bitset_container_intersect((const array_container_t *)c2, + (const bitset_container_t *)c1); + case CONTAINER_PAIR(ARRAY_CONTAINER_TYPE_CODE, + BITSET_CONTAINER_TYPE_CODE): + return array_bitset_container_intersect((const array_container_t *)c1, + (const bitset_container_t *)c2); + case CONTAINER_PAIR(BITSET_CONTAINER_TYPE_CODE, + RUN_CONTAINER_TYPE_CODE): + return run_bitset_container_intersect( + (const run_container_t *)c2, + (const bitset_container_t *)c1); + case CONTAINER_PAIR(RUN_CONTAINER_TYPE_CODE, + BITSET_CONTAINER_TYPE_CODE): + return run_bitset_container_intersect( + (const run_container_t *)c1, + (const bitset_container_t *)c2); + case CONTAINER_PAIR(ARRAY_CONTAINER_TYPE_CODE, RUN_CONTAINER_TYPE_CODE): + return array_run_container_intersect((const array_container_t *)c1, + (const run_container_t *)c2); + case CONTAINER_PAIR(RUN_CONTAINER_TYPE_CODE, ARRAY_CONTAINER_TYPE_CODE): + return array_run_container_intersect((const array_container_t *)c2, + (const run_container_t *)c1); + default: + assert(false); + __builtin_unreachable(); + return 0; + } +} + +/** + * Compute intersection between two containers, with result in the first + container if possible. If the returned pointer is identical to c1, + then the container has been modified. If the returned pointer is different + from c1, then a new container has been created and the caller is responsible + for freeing it. + The type of the first container may change. Returns the modified + (and possibly new) container. +*/ +static inline void *container_iand(void *c1, uint8_t type1, const void *c2, + uint8_t type2, uint8_t *result_type) { + c1 = get_writable_copy_if_shared(c1, &type1); + c2 = container_unwrap_shared(c2, &type2); + void *result = NULL; + switch (CONTAINER_PAIR(type1, type2)) { + case CONTAINER_PAIR(BITSET_CONTAINER_TYPE_CODE, + BITSET_CONTAINER_TYPE_CODE): + *result_type = + bitset_bitset_container_intersection_inplace( + (bitset_container_t *)c1, (const bitset_container_t *)c2, &result) + ? BITSET_CONTAINER_TYPE_CODE + : ARRAY_CONTAINER_TYPE_CODE; + return result; + case CONTAINER_PAIR(ARRAY_CONTAINER_TYPE_CODE, + ARRAY_CONTAINER_TYPE_CODE): + array_container_intersection_inplace((array_container_t *)c1, + (const array_container_t *)c2); + *result_type = ARRAY_CONTAINER_TYPE_CODE; + return c1; + case CONTAINER_PAIR(RUN_CONTAINER_TYPE_CODE, RUN_CONTAINER_TYPE_CODE): + result = run_container_create(); + run_container_intersection((const run_container_t *)c1, + (const run_container_t *)c2, + (run_container_t *)result); + // as of January 2016, Java code used non-in-place intersection for + // two runcontainers + return convert_run_to_efficient_container_and_free( + (run_container_t *)result, result_type); + case CONTAINER_PAIR(BITSET_CONTAINER_TYPE_CODE, + ARRAY_CONTAINER_TYPE_CODE): + // c1 is a bitmap so no inplace possible + result = array_container_create(); + array_bitset_container_intersection((const array_container_t *)c2, + (const bitset_container_t *)c1, + (array_container_t *)result); + *result_type = ARRAY_CONTAINER_TYPE_CODE; // never bitset + return result; + case CONTAINER_PAIR(ARRAY_CONTAINER_TYPE_CODE, + BITSET_CONTAINER_TYPE_CODE): + *result_type = ARRAY_CONTAINER_TYPE_CODE; // never bitset + array_bitset_container_intersection( + (const array_container_t *)c1, (const bitset_container_t *)c2, + (array_container_t *)c1); // allowed + return c1; + + case CONTAINER_PAIR(BITSET_CONTAINER_TYPE_CODE, + RUN_CONTAINER_TYPE_CODE): + // will attempt in-place computation + *result_type = run_bitset_container_intersection( + (const run_container_t *)c2, + (const bitset_container_t *)c1, &c1) + ? BITSET_CONTAINER_TYPE_CODE + : ARRAY_CONTAINER_TYPE_CODE; + return c1; + case CONTAINER_PAIR(RUN_CONTAINER_TYPE_CODE, + BITSET_CONTAINER_TYPE_CODE): + *result_type = run_bitset_container_intersection( + (const run_container_t *)c1, + (const bitset_container_t *)c2, &result) + ? BITSET_CONTAINER_TYPE_CODE + : ARRAY_CONTAINER_TYPE_CODE; + return result; + case CONTAINER_PAIR(ARRAY_CONTAINER_TYPE_CODE, RUN_CONTAINER_TYPE_CODE): + result = array_container_create(); + *result_type = ARRAY_CONTAINER_TYPE_CODE; // never bitset + array_run_container_intersection((const array_container_t *)c1, + (const run_container_t *)c2, + (array_container_t *)result); + return result; + + case CONTAINER_PAIR(RUN_CONTAINER_TYPE_CODE, ARRAY_CONTAINER_TYPE_CODE): + result = array_container_create(); + *result_type = ARRAY_CONTAINER_TYPE_CODE; // never bitset + array_run_container_intersection((const array_container_t *)c2, + (const run_container_t *)c1, + (array_container_t *)result); + return result; + default: + assert(false); + __builtin_unreachable(); + return NULL; + } +} + +/** + * Compute union between two containers, generate a new container (having type + * result_type), requires a typecode. This allocates new memory, caller + * is responsible for deallocation. + */ +static inline void *container_or(const void *c1, uint8_t type1, const void *c2, + uint8_t type2, uint8_t *result_type) { + c1 = container_unwrap_shared(c1, &type1); + c2 = container_unwrap_shared(c2, &type2); + void *result = NULL; + switch (CONTAINER_PAIR(type1, type2)) { + case CONTAINER_PAIR(BITSET_CONTAINER_TYPE_CODE, + BITSET_CONTAINER_TYPE_CODE): + result = bitset_container_create(); + bitset_container_or((const bitset_container_t *)c1, + (const bitset_container_t *)c2, + (bitset_container_t *)result); + *result_type = BITSET_CONTAINER_TYPE_CODE; + return result; + case CONTAINER_PAIR(ARRAY_CONTAINER_TYPE_CODE, + ARRAY_CONTAINER_TYPE_CODE): + *result_type = array_array_container_union( + (const array_container_t *)c1, + (const array_container_t *)c2, &result) + ? BITSET_CONTAINER_TYPE_CODE + : ARRAY_CONTAINER_TYPE_CODE; + return result; + case CONTAINER_PAIR(RUN_CONTAINER_TYPE_CODE, RUN_CONTAINER_TYPE_CODE): + result = run_container_create(); + run_container_union((const run_container_t *)c1, + (const run_container_t *)c2, + (run_container_t *)result); + *result_type = RUN_CONTAINER_TYPE_CODE; + // todo: could be optimized since will never convert to array + result = convert_run_to_efficient_container_and_free( + (run_container_t *)result, (uint8_t *)result_type); + return result; + case CONTAINER_PAIR(BITSET_CONTAINER_TYPE_CODE, + ARRAY_CONTAINER_TYPE_CODE): + result = bitset_container_create(); + array_bitset_container_union((const array_container_t *)c2, + (const bitset_container_t *)c1, + (bitset_container_t *)result); + *result_type = BITSET_CONTAINER_TYPE_CODE; + return result; + case CONTAINER_PAIR(ARRAY_CONTAINER_TYPE_CODE, + BITSET_CONTAINER_TYPE_CODE): + result = bitset_container_create(); + array_bitset_container_union((const array_container_t *)c1, + (const bitset_container_t *)c2, + (bitset_container_t *)result); + *result_type = BITSET_CONTAINER_TYPE_CODE; + return result; + case CONTAINER_PAIR(BITSET_CONTAINER_TYPE_CODE, + RUN_CONTAINER_TYPE_CODE): + if (run_container_is_full((const run_container_t *)c2)) { + result = run_container_create(); + *result_type = RUN_CONTAINER_TYPE_CODE; + run_container_copy((const run_container_t *)c2, + (run_container_t *)result); + return result; + } + result = bitset_container_create(); + run_bitset_container_union((const run_container_t *)c2, + (const bitset_container_t *)c1, + (bitset_container_t *)result); + *result_type = BITSET_CONTAINER_TYPE_CODE; + return result; + case CONTAINER_PAIR(RUN_CONTAINER_TYPE_CODE, + BITSET_CONTAINER_TYPE_CODE): + if (run_container_is_full((const run_container_t *)c1)) { + result = run_container_create(); + *result_type = RUN_CONTAINER_TYPE_CODE; + run_container_copy((const run_container_t *)c1, + (run_container_t *)result); + return result; + } + result = bitset_container_create(); + run_bitset_container_union((const run_container_t *)c1, + (const bitset_container_t *)c2, + (bitset_container_t *)result); + *result_type = BITSET_CONTAINER_TYPE_CODE; + return result; + case CONTAINER_PAIR(ARRAY_CONTAINER_TYPE_CODE, RUN_CONTAINER_TYPE_CODE): + result = run_container_create(); + array_run_container_union((const array_container_t *)c1, + (const run_container_t *)c2, + (run_container_t *)result); + result = convert_run_to_efficient_container_and_free( + (run_container_t *)result, (uint8_t *)result_type); + return result; + case CONTAINER_PAIR(RUN_CONTAINER_TYPE_CODE, ARRAY_CONTAINER_TYPE_CODE): + result = run_container_create(); + array_run_container_union((const array_container_t *)c2, + (const run_container_t *)c1, + (run_container_t *)result); + result = convert_run_to_efficient_container_and_free( + (run_container_t *)result, (uint8_t *)result_type); + return result; + default: + assert(false); + __builtin_unreachable(); + return NULL; // unreached + } +} + +/** + * Compute union between two containers, generate a new container (having type + * result_type), requires a typecode. This allocates new memory, caller + * is responsible for deallocation. + * + * This lazy version delays some operations such as the maintenance of the + * cardinality. It requires repair later on the generated containers. + */ +static inline void *container_lazy_or(const void *c1, uint8_t type1, + const void *c2, uint8_t type2, + uint8_t *result_type) { + c1 = container_unwrap_shared(c1, &type1); + c2 = container_unwrap_shared(c2, &type2); + void *result = NULL; + switch (CONTAINER_PAIR(type1, type2)) { + case CONTAINER_PAIR(BITSET_CONTAINER_TYPE_CODE, + BITSET_CONTAINER_TYPE_CODE): + result = bitset_container_create(); + bitset_container_or_nocard( + (const bitset_container_t *)c1, (const bitset_container_t *)c2, + (bitset_container_t *)result); // is lazy + *result_type = BITSET_CONTAINER_TYPE_CODE; + return result; + case CONTAINER_PAIR(ARRAY_CONTAINER_TYPE_CODE, + ARRAY_CONTAINER_TYPE_CODE): + *result_type = array_array_container_lazy_union( + (const array_container_t *)c1, + (const array_container_t *)c2, &result) + ? BITSET_CONTAINER_TYPE_CODE + : ARRAY_CONTAINER_TYPE_CODE; + return result; + case CONTAINER_PAIR(RUN_CONTAINER_TYPE_CODE, RUN_CONTAINER_TYPE_CODE): + result = run_container_create(); + run_container_union((const run_container_t *)c1, + (const run_container_t *)c2, + (run_container_t *)result); + *result_type = RUN_CONTAINER_TYPE_CODE; + // we are being lazy + result = convert_run_to_efficient_container( + (run_container_t *)result, result_type); + return result; + case CONTAINER_PAIR(BITSET_CONTAINER_TYPE_CODE, + ARRAY_CONTAINER_TYPE_CODE): + result = bitset_container_create(); + array_bitset_container_lazy_union( + (const array_container_t *)c2, (const bitset_container_t *)c1, + (bitset_container_t *)result); // is lazy + *result_type = BITSET_CONTAINER_TYPE_CODE; + return result; + case CONTAINER_PAIR(ARRAY_CONTAINER_TYPE_CODE, + BITSET_CONTAINER_TYPE_CODE): + result = bitset_container_create(); + array_bitset_container_lazy_union( + (const array_container_t *)c1, (const bitset_container_t *)c2, + (bitset_container_t *)result); // is lazy + *result_type = BITSET_CONTAINER_TYPE_CODE; + return result; + case CONTAINER_PAIR(BITSET_CONTAINER_TYPE_CODE, + RUN_CONTAINER_TYPE_CODE): + if (run_container_is_full((const run_container_t *)c2)) { + result = run_container_create(); + *result_type = RUN_CONTAINER_TYPE_CODE; + run_container_copy((const run_container_t *)c2, + (run_container_t *)result); + return result; + } + result = bitset_container_create(); + run_bitset_container_lazy_union( + (const run_container_t *)c2, (const bitset_container_t *)c1, + (bitset_container_t *)result); // is lazy + *result_type = BITSET_CONTAINER_TYPE_CODE; + return result; + case CONTAINER_PAIR(RUN_CONTAINER_TYPE_CODE, + BITSET_CONTAINER_TYPE_CODE): + if (run_container_is_full((const run_container_t *)c1)) { + result = run_container_create(); + *result_type = RUN_CONTAINER_TYPE_CODE; + run_container_copy((const run_container_t *)c1, + (run_container_t *)result); + return result; + } + result = bitset_container_create(); + run_bitset_container_lazy_union( + (const run_container_t *)c1, (const bitset_container_t *)c2, + (bitset_container_t *)result); // is lazy + *result_type = BITSET_CONTAINER_TYPE_CODE; + return result; + case CONTAINER_PAIR(ARRAY_CONTAINER_TYPE_CODE, RUN_CONTAINER_TYPE_CODE): + result = run_container_create(); + array_run_container_union((const array_container_t *)c1, + (const run_container_t *)c2, + (run_container_t *)result); + *result_type = RUN_CONTAINER_TYPE_CODE; + // next line skipped since we are lazy + // result = convert_run_to_efficient_container(result, result_type); + return result; + case CONTAINER_PAIR(RUN_CONTAINER_TYPE_CODE, ARRAY_CONTAINER_TYPE_CODE): + result = run_container_create(); + array_run_container_union( + (const array_container_t *)c2, (const run_container_t *)c1, + (run_container_t *)result); // TODO make lazy + *result_type = RUN_CONTAINER_TYPE_CODE; + // next line skipped since we are lazy + // result = convert_run_to_efficient_container(result, result_type); + return result; + default: + assert(false); + __builtin_unreachable(); + return NULL; // unreached + } +} + +/** + * Compute the union between two containers, with result in the first container. + * If the returned pointer is identical to c1, then the container has been + * modified. + * If the returned pointer is different from c1, then a new container has been + * created and the caller is responsible for freeing it. + * The type of the first container may change. Returns the modified + * (and possibly new) container +*/ +static inline void *container_ior(void *c1, uint8_t type1, const void *c2, + uint8_t type2, uint8_t *result_type) { + c1 = get_writable_copy_if_shared(c1, &type1); + c2 = container_unwrap_shared(c2, &type2); + void *result = NULL; + switch (CONTAINER_PAIR(type1, type2)) { + case CONTAINER_PAIR(BITSET_CONTAINER_TYPE_CODE, + BITSET_CONTAINER_TYPE_CODE): + bitset_container_or((const bitset_container_t *)c1, + (const bitset_container_t *)c2, + (bitset_container_t *)c1); +#ifdef OR_BITSET_CONVERSION_TO_FULL + if (((bitset_container_t *)c1)->cardinality == + (1 << 16)) { // we convert + result = run_container_create_range(0, (1 << 16)); + *result_type = RUN_CONTAINER_TYPE_CODE; + return result; + } +#endif + *result_type = BITSET_CONTAINER_TYPE_CODE; + return c1; + case CONTAINER_PAIR(ARRAY_CONTAINER_TYPE_CODE, + ARRAY_CONTAINER_TYPE_CODE): + *result_type = array_array_container_inplace_union( + (array_container_t *)c1, + (const array_container_t *)c2, &result) + ? BITSET_CONTAINER_TYPE_CODE + : ARRAY_CONTAINER_TYPE_CODE; + if((result == NULL) + && (*result_type == ARRAY_CONTAINER_TYPE_CODE)) { + return c1; // the computation was done in-place! + } + return result; + case CONTAINER_PAIR(RUN_CONTAINER_TYPE_CODE, RUN_CONTAINER_TYPE_CODE): + run_container_union_inplace((run_container_t *)c1, + (const run_container_t *)c2); + return convert_run_to_efficient_container((run_container_t *)c1, + result_type); + case CONTAINER_PAIR(BITSET_CONTAINER_TYPE_CODE, + ARRAY_CONTAINER_TYPE_CODE): + array_bitset_container_union((const array_container_t *)c2, + (const bitset_container_t *)c1, + (bitset_container_t *)c1); + *result_type = BITSET_CONTAINER_TYPE_CODE; // never array + return c1; + case CONTAINER_PAIR(ARRAY_CONTAINER_TYPE_CODE, + BITSET_CONTAINER_TYPE_CODE): + // c1 is an array, so no in-place possible + result = bitset_container_create(); + *result_type = BITSET_CONTAINER_TYPE_CODE; + array_bitset_container_union((const array_container_t *)c1, + (const bitset_container_t *)c2, + (bitset_container_t *)result); + return result; + case CONTAINER_PAIR(BITSET_CONTAINER_TYPE_CODE, + RUN_CONTAINER_TYPE_CODE): + if (run_container_is_full((const run_container_t *)c2)) { + result = run_container_create(); + *result_type = RUN_CONTAINER_TYPE_CODE; + run_container_copy((const run_container_t *)c2, + (run_container_t *)result); + return result; + } + run_bitset_container_union((const run_container_t *)c2, + (const bitset_container_t *)c1, + (bitset_container_t *)c1); // allowed + *result_type = BITSET_CONTAINER_TYPE_CODE; + return c1; + case CONTAINER_PAIR(RUN_CONTAINER_TYPE_CODE, + BITSET_CONTAINER_TYPE_CODE): + if (run_container_is_full((const run_container_t *)c1)) { + *result_type = RUN_CONTAINER_TYPE_CODE; + + return c1; + } + result = bitset_container_create(); + run_bitset_container_union((const run_container_t *)c1, + (const bitset_container_t *)c2, + (bitset_container_t *)result); + *result_type = BITSET_CONTAINER_TYPE_CODE; + return result; + case CONTAINER_PAIR(ARRAY_CONTAINER_TYPE_CODE, RUN_CONTAINER_TYPE_CODE): + result = run_container_create(); + array_run_container_union((const array_container_t *)c1, + (const run_container_t *)c2, + (run_container_t *)result); + result = convert_run_to_efficient_container_and_free( + (run_container_t *)result, result_type); + return result; + case CONTAINER_PAIR(RUN_CONTAINER_TYPE_CODE, ARRAY_CONTAINER_TYPE_CODE): + array_run_container_inplace_union((const array_container_t *)c2, + (run_container_t *)c1); + c1 = convert_run_to_efficient_container((run_container_t *)c1, + result_type); + return c1; + default: + assert(false); + __builtin_unreachable(); + return NULL; + } +} + +/** + * Compute the union between two containers, with result in the first container. + * If the returned pointer is identical to c1, then the container has been + * modified. + * If the returned pointer is different from c1, then a new container has been + * created and the caller is responsible for freeing it. + * The type of the first container may change. Returns the modified + * (and possibly new) container + * + * This lazy version delays some operations such as the maintenance of the + * cardinality. It requires repair later on the generated containers. +*/ +static inline void *container_lazy_ior(void *c1, uint8_t type1, const void *c2, + uint8_t type2, uint8_t *result_type) { + assert(type1 != SHARED_CONTAINER_TYPE_CODE); + // c1 = get_writable_copy_if_shared(c1,&type1); + c2 = container_unwrap_shared(c2, &type2); + void *result = NULL; + switch (CONTAINER_PAIR(type1, type2)) { + case CONTAINER_PAIR(BITSET_CONTAINER_TYPE_CODE, + BITSET_CONTAINER_TYPE_CODE): +#ifdef LAZY_OR_BITSET_CONVERSION_TO_FULL + // if we have two bitsets, we might as well compute the cardinality + bitset_container_or((const bitset_container_t *)c1, + (const bitset_container_t *)c2, + (bitset_container_t *)c1); + // it is possible that two bitsets can lead to a full container + if (((bitset_container_t *)c1)->cardinality == + (1 << 16)) { // we convert + result = run_container_create_range(0, (1 << 16)); + *result_type = RUN_CONTAINER_TYPE_CODE; + return result; + } +#else + bitset_container_or_nocard((const bitset_container_t *)c1, + (const bitset_container_t *)c2, + (bitset_container_t *)c1); + +#endif + *result_type = BITSET_CONTAINER_TYPE_CODE; + return c1; + case CONTAINER_PAIR(ARRAY_CONTAINER_TYPE_CODE, + ARRAY_CONTAINER_TYPE_CODE): + *result_type = array_array_container_lazy_inplace_union( + (array_container_t *)c1, + (const array_container_t *)c2, &result) + ? BITSET_CONTAINER_TYPE_CODE + : ARRAY_CONTAINER_TYPE_CODE; + if((result == NULL) + && (*result_type == ARRAY_CONTAINER_TYPE_CODE)) { + return c1; // the computation was done in-place! + } + return result; + case CONTAINER_PAIR(RUN_CONTAINER_TYPE_CODE, RUN_CONTAINER_TYPE_CODE): + run_container_union_inplace((run_container_t *)c1, + (const run_container_t *)c2); + *result_type = RUN_CONTAINER_TYPE_CODE; + return convert_run_to_efficient_container((run_container_t *)c1, + result_type); + case CONTAINER_PAIR(BITSET_CONTAINER_TYPE_CODE, + ARRAY_CONTAINER_TYPE_CODE): + array_bitset_container_lazy_union( + (const array_container_t *)c2, (const bitset_container_t *)c1, + (bitset_container_t *)c1); // is lazy + *result_type = BITSET_CONTAINER_TYPE_CODE; // never array + return c1; + case CONTAINER_PAIR(ARRAY_CONTAINER_TYPE_CODE, + BITSET_CONTAINER_TYPE_CODE): + // c1 is an array, so no in-place possible + result = bitset_container_create(); + *result_type = BITSET_CONTAINER_TYPE_CODE; + array_bitset_container_lazy_union( + (const array_container_t *)c1, (const bitset_container_t *)c2, + (bitset_container_t *)result); // is lazy + return result; + case CONTAINER_PAIR(BITSET_CONTAINER_TYPE_CODE, + RUN_CONTAINER_TYPE_CODE): + if (run_container_is_full((const run_container_t *)c2)) { + result = run_container_create(); + *result_type = RUN_CONTAINER_TYPE_CODE; + run_container_copy((const run_container_t *)c2, + (run_container_t *)result); + return result; + } + run_bitset_container_lazy_union( + (const run_container_t *)c2, (const bitset_container_t *)c1, + (bitset_container_t *)c1); // allowed // lazy + *result_type = BITSET_CONTAINER_TYPE_CODE; + return c1; + case CONTAINER_PAIR(RUN_CONTAINER_TYPE_CODE, + BITSET_CONTAINER_TYPE_CODE): + if (run_container_is_full((const run_container_t *)c1)) { + *result_type = RUN_CONTAINER_TYPE_CODE; + return c1; + } + result = bitset_container_create(); + run_bitset_container_lazy_union( + (const run_container_t *)c1, (const bitset_container_t *)c2, + (bitset_container_t *)result); // lazy + *result_type = BITSET_CONTAINER_TYPE_CODE; + return result; + case CONTAINER_PAIR(ARRAY_CONTAINER_TYPE_CODE, RUN_CONTAINER_TYPE_CODE): + result = run_container_create(); + array_run_container_union((const array_container_t *)c1, + (const run_container_t *)c2, + (run_container_t *)result); + *result_type = RUN_CONTAINER_TYPE_CODE; + // next line skipped since we are lazy + // result = convert_run_to_efficient_container_and_free(result, + // result_type); + return result; + case CONTAINER_PAIR(RUN_CONTAINER_TYPE_CODE, ARRAY_CONTAINER_TYPE_CODE): + array_run_container_inplace_union((const array_container_t *)c2, + (run_container_t *)c1); + *result_type = RUN_CONTAINER_TYPE_CODE; + // next line skipped since we are lazy + // result = convert_run_to_efficient_container_and_free(result, + // result_type); + return c1; + default: + assert(false); + __builtin_unreachable(); + return NULL; + } +} + +/** + * Compute symmetric difference (xor) between two containers, generate a new + * container (having type result_type), requires a typecode. This allocates new + * memory, caller is responsible for deallocation. + */ +static inline void *container_xor(const void *c1, uint8_t type1, const void *c2, + uint8_t type2, uint8_t *result_type) { + c1 = container_unwrap_shared(c1, &type1); + c2 = container_unwrap_shared(c2, &type2); + void *result = NULL; + switch (CONTAINER_PAIR(type1, type2)) { + case CONTAINER_PAIR(BITSET_CONTAINER_TYPE_CODE, + BITSET_CONTAINER_TYPE_CODE): + *result_type = bitset_bitset_container_xor( + (const bitset_container_t *)c1, + (const bitset_container_t *)c2, &result) + ? BITSET_CONTAINER_TYPE_CODE + : ARRAY_CONTAINER_TYPE_CODE; + return result; + case CONTAINER_PAIR(ARRAY_CONTAINER_TYPE_CODE, + ARRAY_CONTAINER_TYPE_CODE): + *result_type = array_array_container_xor( + (const array_container_t *)c1, + (const array_container_t *)c2, &result) + ? BITSET_CONTAINER_TYPE_CODE + : ARRAY_CONTAINER_TYPE_CODE; + return result; + case CONTAINER_PAIR(RUN_CONTAINER_TYPE_CODE, RUN_CONTAINER_TYPE_CODE): + *result_type = + run_run_container_xor((const run_container_t *)c1, + (const run_container_t *)c2, &result); + return result; + + case CONTAINER_PAIR(BITSET_CONTAINER_TYPE_CODE, + ARRAY_CONTAINER_TYPE_CODE): + *result_type = array_bitset_container_xor( + (const array_container_t *)c2, + (const bitset_container_t *)c1, &result) + ? BITSET_CONTAINER_TYPE_CODE + : ARRAY_CONTAINER_TYPE_CODE; + return result; + case CONTAINER_PAIR(ARRAY_CONTAINER_TYPE_CODE, + BITSET_CONTAINER_TYPE_CODE): + *result_type = array_bitset_container_xor( + (const array_container_t *)c1, + (const bitset_container_t *)c2, &result) + ? BITSET_CONTAINER_TYPE_CODE + : ARRAY_CONTAINER_TYPE_CODE; + return result; + case CONTAINER_PAIR(BITSET_CONTAINER_TYPE_CODE, + RUN_CONTAINER_TYPE_CODE): + *result_type = run_bitset_container_xor( + (const run_container_t *)c2, + (const bitset_container_t *)c1, &result) + ? BITSET_CONTAINER_TYPE_CODE + : ARRAY_CONTAINER_TYPE_CODE; + return result; + + case CONTAINER_PAIR(RUN_CONTAINER_TYPE_CODE, + BITSET_CONTAINER_TYPE_CODE): + + *result_type = run_bitset_container_xor( + (const run_container_t *)c1, + (const bitset_container_t *)c2, &result) + ? BITSET_CONTAINER_TYPE_CODE + : ARRAY_CONTAINER_TYPE_CODE; + return result; + + case CONTAINER_PAIR(ARRAY_CONTAINER_TYPE_CODE, RUN_CONTAINER_TYPE_CODE): + *result_type = + array_run_container_xor((const array_container_t *)c1, + (const run_container_t *)c2, &result); + return result; + + case CONTAINER_PAIR(RUN_CONTAINER_TYPE_CODE, ARRAY_CONTAINER_TYPE_CODE): + *result_type = + array_run_container_xor((const array_container_t *)c2, + (const run_container_t *)c1, &result); + return result; + + default: + assert(false); + __builtin_unreachable(); + return NULL; // unreached + } +} + +/** + * Compute xor between two containers, generate a new container (having type + * result_type), requires a typecode. This allocates new memory, caller + * is responsible for deallocation. + * + * This lazy version delays some operations such as the maintenance of the + * cardinality. It requires repair later on the generated containers. + */ +static inline void *container_lazy_xor(const void *c1, uint8_t type1, + const void *c2, uint8_t type2, + uint8_t *result_type) { + c1 = container_unwrap_shared(c1, &type1); + c2 = container_unwrap_shared(c2, &type2); + void *result = NULL; + switch (CONTAINER_PAIR(type1, type2)) { + case CONTAINER_PAIR(BITSET_CONTAINER_TYPE_CODE, + BITSET_CONTAINER_TYPE_CODE): + result = bitset_container_create(); + bitset_container_xor_nocard( + (const bitset_container_t *)c1, (const bitset_container_t *)c2, + (bitset_container_t *)result); // is lazy + *result_type = BITSET_CONTAINER_TYPE_CODE; + return result; + case CONTAINER_PAIR(ARRAY_CONTAINER_TYPE_CODE, + ARRAY_CONTAINER_TYPE_CODE): + *result_type = array_array_container_lazy_xor( + (const array_container_t *)c1, + (const array_container_t *)c2, &result) + ? BITSET_CONTAINER_TYPE_CODE + : ARRAY_CONTAINER_TYPE_CODE; + return result; + case CONTAINER_PAIR(RUN_CONTAINER_TYPE_CODE, RUN_CONTAINER_TYPE_CODE): + // nothing special done yet. + *result_type = + run_run_container_xor((const run_container_t *)c1, + (const run_container_t *)c2, &result); + return result; + case CONTAINER_PAIR(BITSET_CONTAINER_TYPE_CODE, + ARRAY_CONTAINER_TYPE_CODE): + result = bitset_container_create(); + *result_type = BITSET_CONTAINER_TYPE_CODE; + array_bitset_container_lazy_xor((const array_container_t *)c2, + (const bitset_container_t *)c1, + (bitset_container_t *)result); + return result; + case CONTAINER_PAIR(ARRAY_CONTAINER_TYPE_CODE, + BITSET_CONTAINER_TYPE_CODE): + result = bitset_container_create(); + *result_type = BITSET_CONTAINER_TYPE_CODE; + array_bitset_container_lazy_xor((const array_container_t *)c1, + (const bitset_container_t *)c2, + (bitset_container_t *)result); + return result; + case CONTAINER_PAIR(BITSET_CONTAINER_TYPE_CODE, + RUN_CONTAINER_TYPE_CODE): + result = bitset_container_create(); + run_bitset_container_lazy_xor((const run_container_t *)c2, + (const bitset_container_t *)c1, + (bitset_container_t *)result); + *result_type = BITSET_CONTAINER_TYPE_CODE; + return result; + case CONTAINER_PAIR(RUN_CONTAINER_TYPE_CODE, + BITSET_CONTAINER_TYPE_CODE): + result = bitset_container_create(); + run_bitset_container_lazy_xor((const run_container_t *)c1, + (const bitset_container_t *)c2, + (bitset_container_t *)result); + *result_type = BITSET_CONTAINER_TYPE_CODE; + return result; + + case CONTAINER_PAIR(ARRAY_CONTAINER_TYPE_CODE, RUN_CONTAINER_TYPE_CODE): + result = run_container_create(); + array_run_container_lazy_xor((const array_container_t *)c1, + (const run_container_t *)c2, + (run_container_t *)result); + *result_type = RUN_CONTAINER_TYPE_CODE; + // next line skipped since we are lazy + // result = convert_run_to_efficient_container(result, result_type); + return result; + case CONTAINER_PAIR(RUN_CONTAINER_TYPE_CODE, ARRAY_CONTAINER_TYPE_CODE): + result = run_container_create(); + array_run_container_lazy_xor((const array_container_t *)c2, + (const run_container_t *)c1, + (run_container_t *)result); + *result_type = RUN_CONTAINER_TYPE_CODE; + // next line skipped since we are lazy + // result = convert_run_to_efficient_container(result, result_type); + return result; + default: + assert(false); + __builtin_unreachable(); + return NULL; // unreached + } +} + +/** + * Compute the xor between two containers, with result in the first container. + * If the returned pointer is identical to c1, then the container has been + * modified. + * If the returned pointer is different from c1, then a new container has been + * created and the caller is responsible for freeing it. + * The type of the first container may change. Returns the modified + * (and possibly new) container +*/ +static inline void *container_ixor(void *c1, uint8_t type1, const void *c2, + uint8_t type2, uint8_t *result_type) { + c1 = get_writable_copy_if_shared(c1, &type1); + c2 = container_unwrap_shared(c2, &type2); + void *result = NULL; + switch (CONTAINER_PAIR(type1, type2)) { + case CONTAINER_PAIR(BITSET_CONTAINER_TYPE_CODE, + BITSET_CONTAINER_TYPE_CODE): + *result_type = bitset_bitset_container_ixor( + (bitset_container_t *)c1, + (const bitset_container_t *)c2, &result) + ? BITSET_CONTAINER_TYPE_CODE + : ARRAY_CONTAINER_TYPE_CODE; + return result; + case CONTAINER_PAIR(ARRAY_CONTAINER_TYPE_CODE, + ARRAY_CONTAINER_TYPE_CODE): + *result_type = array_array_container_ixor( + (array_container_t *)c1, + (const array_container_t *)c2, &result) + ? BITSET_CONTAINER_TYPE_CODE + : ARRAY_CONTAINER_TYPE_CODE; + return result; + + case CONTAINER_PAIR(RUN_CONTAINER_TYPE_CODE, RUN_CONTAINER_TYPE_CODE): + *result_type = run_run_container_ixor( + (run_container_t *)c1, (const run_container_t *)c2, &result); + return result; + + case CONTAINER_PAIR(BITSET_CONTAINER_TYPE_CODE, + ARRAY_CONTAINER_TYPE_CODE): + *result_type = bitset_array_container_ixor( + (bitset_container_t *)c1, + (const array_container_t *)c2, &result) + ? BITSET_CONTAINER_TYPE_CODE + : ARRAY_CONTAINER_TYPE_CODE; + return result; + case CONTAINER_PAIR(ARRAY_CONTAINER_TYPE_CODE, + BITSET_CONTAINER_TYPE_CODE): + *result_type = array_bitset_container_ixor( + (array_container_t *)c1, + (const bitset_container_t *)c2, &result) + ? BITSET_CONTAINER_TYPE_CODE + : ARRAY_CONTAINER_TYPE_CODE; + + return result; + + case CONTAINER_PAIR(BITSET_CONTAINER_TYPE_CODE, + RUN_CONTAINER_TYPE_CODE): + *result_type = + bitset_run_container_ixor((bitset_container_t *)c1, + (const run_container_t *)c2, &result) + ? BITSET_CONTAINER_TYPE_CODE + : ARRAY_CONTAINER_TYPE_CODE; + + return result; + + case CONTAINER_PAIR(RUN_CONTAINER_TYPE_CODE, + BITSET_CONTAINER_TYPE_CODE): + *result_type = run_bitset_container_ixor( + (run_container_t *)c1, + (const bitset_container_t *)c2, &result) + ? BITSET_CONTAINER_TYPE_CODE + : ARRAY_CONTAINER_TYPE_CODE; + + return result; + + case CONTAINER_PAIR(ARRAY_CONTAINER_TYPE_CODE, RUN_CONTAINER_TYPE_CODE): + *result_type = array_run_container_ixor( + (array_container_t *)c1, (const run_container_t *)c2, &result); + return result; + case CONTAINER_PAIR(RUN_CONTAINER_TYPE_CODE, ARRAY_CONTAINER_TYPE_CODE): + *result_type = run_array_container_ixor( + (run_container_t *)c1, (const array_container_t *)c2, &result); + return result; + default: + assert(false); + __builtin_unreachable(); + return NULL; + } +} + +/** + * Compute the xor between two containers, with result in the first container. + * If the returned pointer is identical to c1, then the container has been + * modified. + * If the returned pointer is different from c1, then a new container has been + * created and the caller is responsible for freeing it. + * The type of the first container may change. Returns the modified + * (and possibly new) container + * + * This lazy version delays some operations such as the maintenance of the + * cardinality. It requires repair later on the generated containers. +*/ +static inline void *container_lazy_ixor(void *c1, uint8_t type1, const void *c2, + uint8_t type2, uint8_t *result_type) { + assert(type1 != SHARED_CONTAINER_TYPE_CODE); + // c1 = get_writable_copy_if_shared(c1,&type1); + c2 = container_unwrap_shared(c2, &type2); + switch (CONTAINER_PAIR(type1, type2)) { + case CONTAINER_PAIR(BITSET_CONTAINER_TYPE_CODE, + BITSET_CONTAINER_TYPE_CODE): + bitset_container_xor_nocard((bitset_container_t *)c1, + (const bitset_container_t *)c2, + (bitset_container_t *)c1); // is lazy + *result_type = BITSET_CONTAINER_TYPE_CODE; + return c1; + // TODO: other cases being lazy, esp. when we know inplace not likely + // could see the corresponding code for union + default: + // we may have a dirty bitset (without a precomputed cardinality) and + // calling container_ixor on it might be unsafe. + if( (type1 == BITSET_CONTAINER_TYPE_CODE) + && (((const bitset_container_t *)c1)->cardinality == BITSET_UNKNOWN_CARDINALITY)) { + ((bitset_container_t *)c1)->cardinality = bitset_container_compute_cardinality((bitset_container_t *)c1); + } + return container_ixor(c1, type1, c2, type2, result_type); + } +} + +/** + * Compute difference (andnot) between two containers, generate a new + * container (having type result_type), requires a typecode. This allocates new + * memory, caller is responsible for deallocation. + */ +static inline void *container_andnot(const void *c1, uint8_t type1, + const void *c2, uint8_t type2, + uint8_t *result_type) { + c1 = container_unwrap_shared(c1, &type1); + c2 = container_unwrap_shared(c2, &type2); + void *result = NULL; + switch (CONTAINER_PAIR(type1, type2)) { + case CONTAINER_PAIR(BITSET_CONTAINER_TYPE_CODE, + BITSET_CONTAINER_TYPE_CODE): + *result_type = bitset_bitset_container_andnot( + (const bitset_container_t *)c1, + (const bitset_container_t *)c2, &result) + ? BITSET_CONTAINER_TYPE_CODE + : ARRAY_CONTAINER_TYPE_CODE; + return result; + case CONTAINER_PAIR(ARRAY_CONTAINER_TYPE_CODE, + ARRAY_CONTAINER_TYPE_CODE): + result = array_container_create(); + array_array_container_andnot((const array_container_t *)c1, + (const array_container_t *)c2, + (array_container_t *)result); + *result_type = ARRAY_CONTAINER_TYPE_CODE; + return result; + case CONTAINER_PAIR(RUN_CONTAINER_TYPE_CODE, RUN_CONTAINER_TYPE_CODE): + if (run_container_is_full((const run_container_t *)c2)) { + result = array_container_create(); + *result_type = ARRAY_CONTAINER_TYPE_CODE; + return result; + } + *result_type = + run_run_container_andnot((const run_container_t *)c1, + (const run_container_t *)c2, &result); + return result; + + case CONTAINER_PAIR(BITSET_CONTAINER_TYPE_CODE, + ARRAY_CONTAINER_TYPE_CODE): + *result_type = bitset_array_container_andnot( + (const bitset_container_t *)c1, + (const array_container_t *)c2, &result) + ? BITSET_CONTAINER_TYPE_CODE + : ARRAY_CONTAINER_TYPE_CODE; + return result; + case CONTAINER_PAIR(ARRAY_CONTAINER_TYPE_CODE, + BITSET_CONTAINER_TYPE_CODE): + result = array_container_create(); + array_bitset_container_andnot((const array_container_t *)c1, + (const bitset_container_t *)c2, + (array_container_t *)result); + *result_type = ARRAY_CONTAINER_TYPE_CODE; + return result; + case CONTAINER_PAIR(BITSET_CONTAINER_TYPE_CODE, + RUN_CONTAINER_TYPE_CODE): + if (run_container_is_full((const run_container_t *)c2)) { + result = array_container_create(); + *result_type = ARRAY_CONTAINER_TYPE_CODE; + return result; + } + *result_type = bitset_run_container_andnot( + (const bitset_container_t *)c1, + (const run_container_t *)c2, &result) + ? BITSET_CONTAINER_TYPE_CODE + : ARRAY_CONTAINER_TYPE_CODE; + return result; + case CONTAINER_PAIR(RUN_CONTAINER_TYPE_CODE, + BITSET_CONTAINER_TYPE_CODE): + + *result_type = run_bitset_container_andnot( + (const run_container_t *)c1, + (const bitset_container_t *)c2, &result) + ? BITSET_CONTAINER_TYPE_CODE + : ARRAY_CONTAINER_TYPE_CODE; + return result; + + case CONTAINER_PAIR(ARRAY_CONTAINER_TYPE_CODE, RUN_CONTAINER_TYPE_CODE): + if (run_container_is_full((const run_container_t *)c2)) { + result = array_container_create(); + *result_type = ARRAY_CONTAINER_TYPE_CODE; + return result; + } + result = array_container_create(); + array_run_container_andnot((const array_container_t *)c1, + (const run_container_t *)c2, + (array_container_t *)result); + *result_type = ARRAY_CONTAINER_TYPE_CODE; + return result; + + case CONTAINER_PAIR(RUN_CONTAINER_TYPE_CODE, ARRAY_CONTAINER_TYPE_CODE): + *result_type = run_array_container_andnot( + (const run_container_t *)c1, (const array_container_t *)c2, + &result); + return result; + + default: + assert(false); + __builtin_unreachable(); + return NULL; // unreached + } +} + +/** + * Compute the andnot between two containers, with result in the first + * container. + * If the returned pointer is identical to c1, then the container has been + * modified. + * If the returned pointer is different from c1, then a new container has been + * created and the caller is responsible for freeing it. + * The type of the first container may change. Returns the modified + * (and possibly new) container +*/ +static inline void *container_iandnot(void *c1, uint8_t type1, const void *c2, + uint8_t type2, uint8_t *result_type) { + c1 = get_writable_copy_if_shared(c1, &type1); + c2 = container_unwrap_shared(c2, &type2); + void *result = NULL; + switch (CONTAINER_PAIR(type1, type2)) { + case CONTAINER_PAIR(BITSET_CONTAINER_TYPE_CODE, + BITSET_CONTAINER_TYPE_CODE): + *result_type = bitset_bitset_container_iandnot( + (bitset_container_t *)c1, + (const bitset_container_t *)c2, &result) + ? BITSET_CONTAINER_TYPE_CODE + : ARRAY_CONTAINER_TYPE_CODE; + return result; + case CONTAINER_PAIR(ARRAY_CONTAINER_TYPE_CODE, + ARRAY_CONTAINER_TYPE_CODE): + array_array_container_iandnot((array_container_t *)c1, + (const array_container_t *)c2); + *result_type = ARRAY_CONTAINER_TYPE_CODE; + return c1; + + case CONTAINER_PAIR(RUN_CONTAINER_TYPE_CODE, RUN_CONTAINER_TYPE_CODE): + *result_type = run_run_container_iandnot( + (run_container_t *)c1, (const run_container_t *)c2, &result); + return result; + + case CONTAINER_PAIR(BITSET_CONTAINER_TYPE_CODE, + ARRAY_CONTAINER_TYPE_CODE): + *result_type = bitset_array_container_iandnot( + (bitset_container_t *)c1, + (const array_container_t *)c2, &result) + ? BITSET_CONTAINER_TYPE_CODE + : ARRAY_CONTAINER_TYPE_CODE; + return result; + case CONTAINER_PAIR(ARRAY_CONTAINER_TYPE_CODE, + BITSET_CONTAINER_TYPE_CODE): + *result_type = ARRAY_CONTAINER_TYPE_CODE; + + array_bitset_container_iandnot((array_container_t *)c1, + (const bitset_container_t *)c2); + return c1; + + case CONTAINER_PAIR(BITSET_CONTAINER_TYPE_CODE, + RUN_CONTAINER_TYPE_CODE): + *result_type = bitset_run_container_iandnot( + (bitset_container_t *)c1, + (const run_container_t *)c2, &result) + ? BITSET_CONTAINER_TYPE_CODE + : ARRAY_CONTAINER_TYPE_CODE; + + return result; + + case CONTAINER_PAIR(RUN_CONTAINER_TYPE_CODE, + BITSET_CONTAINER_TYPE_CODE): + *result_type = run_bitset_container_iandnot( + (run_container_t *)c1, + (const bitset_container_t *)c2, &result) + ? BITSET_CONTAINER_TYPE_CODE + : ARRAY_CONTAINER_TYPE_CODE; + + return result; + + case CONTAINER_PAIR(ARRAY_CONTAINER_TYPE_CODE, RUN_CONTAINER_TYPE_CODE): + *result_type = ARRAY_CONTAINER_TYPE_CODE; + array_run_container_iandnot((array_container_t *)c1, + (const run_container_t *)c2); + return c1; + case CONTAINER_PAIR(RUN_CONTAINER_TYPE_CODE, ARRAY_CONTAINER_TYPE_CODE): + *result_type = run_array_container_iandnot( + (run_container_t *)c1, (const array_container_t *)c2, &result); + return result; + default: + assert(false); + __builtin_unreachable(); + return NULL; + } +} + +/** + * Visit all values x of the container once, passing (base+x,ptr) + * to iterator. You need to specify a container and its type. + * Returns true if the iteration should continue. + */ +static inline bool container_iterate(const void *container, uint8_t typecode, + uint32_t base, roaring_iterator iterator, + void *ptr) { + container = container_unwrap_shared(container, &typecode); + switch (typecode) { + case BITSET_CONTAINER_TYPE_CODE: + return bitset_container_iterate( + (const bitset_container_t *)container, base, iterator, ptr); + case ARRAY_CONTAINER_TYPE_CODE: + return array_container_iterate((const array_container_t *)container, + base, iterator, ptr); + case RUN_CONTAINER_TYPE_CODE: + return run_container_iterate((const run_container_t *)container, + base, iterator, ptr); + case SHARED_CONTAINER_TYPE_CODE: + default: + assert(false); + __builtin_unreachable(); + return false; + } +} + +static inline bool container_iterate64(const void *container, uint8_t typecode, + uint32_t base, + roaring_iterator64 iterator, + uint64_t high_bits, void *ptr) { + container = container_unwrap_shared(container, &typecode); + switch (typecode) { + case BITSET_CONTAINER_TYPE_CODE: + return bitset_container_iterate64( + (const bitset_container_t *)container, base, iterator, + high_bits, ptr); + case ARRAY_CONTAINER_TYPE_CODE: + return array_container_iterate64( + (const array_container_t *)container, base, iterator, high_bits, + ptr); + case RUN_CONTAINER_TYPE_CODE: + return run_container_iterate64((const run_container_t *)container, + base, iterator, high_bits, ptr); + case SHARED_CONTAINER_TYPE_CODE: + default: + assert(false); + __builtin_unreachable(); + return false; + } +} + +static inline void *container_not(const void *c, uint8_t typ, + uint8_t *result_type) { + c = container_unwrap_shared(c, &typ); + void *result = NULL; + switch (typ) { + case BITSET_CONTAINER_TYPE_CODE: + *result_type = bitset_container_negation( + (const bitset_container_t *)c, &result) + ? BITSET_CONTAINER_TYPE_CODE + : ARRAY_CONTAINER_TYPE_CODE; + return result; + case ARRAY_CONTAINER_TYPE_CODE: + result = bitset_container_create(); + *result_type = BITSET_CONTAINER_TYPE_CODE; + array_container_negation((const array_container_t *)c, + (bitset_container_t *)result); + return result; + case RUN_CONTAINER_TYPE_CODE: + *result_type = + run_container_negation((const run_container_t *)c, &result); + return result; + + case SHARED_CONTAINER_TYPE_CODE: + default: + assert(false); + __builtin_unreachable(); + return NULL; + } +} + +static inline void *container_not_range(const void *c, uint8_t typ, + uint32_t range_start, + uint32_t range_end, + uint8_t *result_type) { + c = container_unwrap_shared(c, &typ); + void *result = NULL; + switch (typ) { + case BITSET_CONTAINER_TYPE_CODE: + *result_type = + bitset_container_negation_range((const bitset_container_t *)c, + range_start, range_end, &result) + ? BITSET_CONTAINER_TYPE_CODE + : ARRAY_CONTAINER_TYPE_CODE; + return result; + case ARRAY_CONTAINER_TYPE_CODE: + *result_type = + array_container_negation_range((const array_container_t *)c, + range_start, range_end, &result) + ? BITSET_CONTAINER_TYPE_CODE + : ARRAY_CONTAINER_TYPE_CODE; + return result; + case RUN_CONTAINER_TYPE_CODE: + *result_type = run_container_negation_range( + (const run_container_t *)c, range_start, range_end, &result); + return result; + + case SHARED_CONTAINER_TYPE_CODE: + default: + assert(false); + __builtin_unreachable(); + return NULL; + } +} + +static inline void *container_inot(void *c, uint8_t typ, uint8_t *result_type) { + c = get_writable_copy_if_shared(c, &typ); + void *result = NULL; + switch (typ) { + case BITSET_CONTAINER_TYPE_CODE: + *result_type = bitset_container_negation_inplace( + (bitset_container_t *)c, &result) + ? BITSET_CONTAINER_TYPE_CODE + : ARRAY_CONTAINER_TYPE_CODE; + return result; + case ARRAY_CONTAINER_TYPE_CODE: + // will never be inplace + result = bitset_container_create(); + *result_type = BITSET_CONTAINER_TYPE_CODE; + array_container_negation((array_container_t *)c, + (bitset_container_t *)result); + array_container_free((array_container_t *)c); + return result; + case RUN_CONTAINER_TYPE_CODE: + *result_type = + run_container_negation_inplace((run_container_t *)c, &result); + return result; + + case SHARED_CONTAINER_TYPE_CODE: + default: + assert(false); + __builtin_unreachable(); + return NULL; + } +} + +static inline void *container_inot_range(void *c, uint8_t typ, + uint32_t range_start, + uint32_t range_end, + uint8_t *result_type) { + c = get_writable_copy_if_shared(c, &typ); + void *result = NULL; + switch (typ) { + case BITSET_CONTAINER_TYPE_CODE: + *result_type = + bitset_container_negation_range_inplace( + (bitset_container_t *)c, range_start, range_end, &result) + ? BITSET_CONTAINER_TYPE_CODE + : ARRAY_CONTAINER_TYPE_CODE; + return result; + case ARRAY_CONTAINER_TYPE_CODE: + *result_type = + array_container_negation_range_inplace( + (array_container_t *)c, range_start, range_end, &result) + ? BITSET_CONTAINER_TYPE_CODE + : ARRAY_CONTAINER_TYPE_CODE; + return result; + case RUN_CONTAINER_TYPE_CODE: + *result_type = run_container_negation_range_inplace( + (run_container_t *)c, range_start, range_end, &result); + return result; + + case SHARED_CONTAINER_TYPE_CODE: + default: + assert(false); + __builtin_unreachable(); + return NULL; + } +} + +/** + * If the element of given rank is in this container, supposing that + * the first + * element has rank start_rank, then the function returns true and + * sets element + * accordingly. + * Otherwise, it returns false and update start_rank. + */ +static inline bool container_select(const void *container, uint8_t typecode, + uint32_t *start_rank, uint32_t rank, + uint32_t *element) { + container = container_unwrap_shared(container, &typecode); + switch (typecode) { + case BITSET_CONTAINER_TYPE_CODE: + return bitset_container_select((const bitset_container_t *)container, + start_rank, rank, element); + case ARRAY_CONTAINER_TYPE_CODE: + return array_container_select((const array_container_t *)container, + start_rank, rank, element); + case RUN_CONTAINER_TYPE_CODE: + return run_container_select((const run_container_t *)container, + start_rank, rank, element); + case SHARED_CONTAINER_TYPE_CODE: + default: + assert(false); + __builtin_unreachable(); + return false; + } +} + +static inline uint16_t container_maximum(const void *container, + uint8_t typecode) { + container = container_unwrap_shared(container, &typecode); + switch (typecode) { + case BITSET_CONTAINER_TYPE_CODE: + return bitset_container_maximum((const bitset_container_t *)container); + case ARRAY_CONTAINER_TYPE_CODE: + return array_container_maximum((const array_container_t *)container); + case RUN_CONTAINER_TYPE_CODE: + return run_container_maximum((const run_container_t *)container); + case SHARED_CONTAINER_TYPE_CODE: + default: + assert(false); + __builtin_unreachable(); + return false; + } +} + +static inline uint16_t container_minimum(const void *container, + uint8_t typecode) { + container = container_unwrap_shared(container, &typecode); + switch (typecode) { + case BITSET_CONTAINER_TYPE_CODE: + return bitset_container_minimum((const bitset_container_t *)container); + case ARRAY_CONTAINER_TYPE_CODE: + return array_container_minimum((const array_container_t *)container); + case RUN_CONTAINER_TYPE_CODE: + return run_container_minimum((const run_container_t *)container); + case SHARED_CONTAINER_TYPE_CODE: + default: + assert(false); + __builtin_unreachable(); + return false; + } +} + +// number of values smaller or equal to x +static inline int container_rank(const void *container, uint8_t typecode, + uint16_t x) { + container = container_unwrap_shared(container, &typecode); + switch (typecode) { + case BITSET_CONTAINER_TYPE_CODE: + return bitset_container_rank((const bitset_container_t *)container, x); + case ARRAY_CONTAINER_TYPE_CODE: + return array_container_rank((const array_container_t *)container, x); + case RUN_CONTAINER_TYPE_CODE: + return run_container_rank((const run_container_t *)container, x); + case SHARED_CONTAINER_TYPE_CODE: + default: + assert(false); + __builtin_unreachable(); + return false; + } +} + +/** + * Add all values in range [min, max] to a given container. + * + * If the returned pointer is different from $container, then a new container + * has been created and the caller is responsible for freeing it. + * The type of the first container may change. Returns the modified + * (and possibly new) container. + */ +static inline void *container_add_range(void *container, uint8_t type, + uint32_t min, uint32_t max, + uint8_t *result_type) { + // NB: when selecting new container type, we perform only inexpensive checks + switch (type) { + case BITSET_CONTAINER_TYPE_CODE: { + bitset_container_t *bitset = (bitset_container_t *) container; + + int32_t union_cardinality = 0; + union_cardinality += bitset->cardinality; + union_cardinality += max - min + 1; + union_cardinality -= bitset_lenrange_cardinality(bitset->array, min, max-min); + + if (union_cardinality == INT32_C(0x10000)) { + *result_type = RUN_CONTAINER_TYPE_CODE; + return run_container_create_range(0, INT32_C(0x10000)); + } else { + *result_type = BITSET_CONTAINER_TYPE_CODE; + bitset_set_lenrange(bitset->array, min, max - min); + bitset->cardinality = union_cardinality; + return bitset; + } + } + case ARRAY_CONTAINER_TYPE_CODE: { + array_container_t *array = (array_container_t *) container; + + int32_t nvals_greater = count_greater(array->array, array->cardinality, max); + int32_t nvals_less = count_less(array->array, array->cardinality - nvals_greater, min); + int32_t union_cardinality = nvals_less + (max - min + 1) + nvals_greater; + + if (union_cardinality == INT32_C(0x10000)) { + *result_type = RUN_CONTAINER_TYPE_CODE; + return run_container_create_range(0, INT32_C(0x10000)); + } else if (union_cardinality <= DEFAULT_MAX_SIZE) { + *result_type = ARRAY_CONTAINER_TYPE_CODE; + array_container_add_range_nvals(array, min, max, nvals_less, nvals_greater); + return array; + } else { + *result_type = BITSET_CONTAINER_TYPE_CODE; + bitset_container_t *bitset = bitset_container_from_array(array); + bitset_set_lenrange(bitset->array, min, max - min); + bitset->cardinality = union_cardinality; + return bitset; + } + } + case RUN_CONTAINER_TYPE_CODE: { + run_container_t *run = (run_container_t *) container; + + int32_t nruns_greater = rle16_count_greater(run->runs, run->n_runs, max); + int32_t nruns_less = rle16_count_less(run->runs, run->n_runs - nruns_greater, min); + + int32_t run_size_bytes = (nruns_less + 1 + nruns_greater) * sizeof(rle16_t); + int32_t bitset_size_bytes = BITSET_CONTAINER_SIZE_IN_WORDS * sizeof(uint64_t); + + if (run_size_bytes <= bitset_size_bytes) { + run_container_add_range_nruns(run, min, max, nruns_less, nruns_greater); + *result_type = RUN_CONTAINER_TYPE_CODE; + return run; + } else { + *result_type = BITSET_CONTAINER_TYPE_CODE; + return bitset_container_from_run_range(run, min, max); + } + } + case SHARED_CONTAINER_TYPE_CODE: + default: + __builtin_unreachable(); + } +} + +/* + * Removes all elements in range [min, max]. + * Returns one of: + * - NULL if no elements left + * - pointer to the original container + * - pointer to a newly-allocated container (if it is more efficient) + * + * If the returned pointer is different from $container, then a new container + * has been created and the caller is responsible for freeing the original container. + */ +static inline void *container_remove_range(void *container, uint8_t type, + uint32_t min, uint32_t max, + uint8_t *result_type) { + switch (type) { + case BITSET_CONTAINER_TYPE_CODE: { + bitset_container_t *bitset = (bitset_container_t *) container; + + int32_t result_cardinality = bitset->cardinality - + bitset_lenrange_cardinality(bitset->array, min, max-min); + + if (result_cardinality == 0) { + return NULL; + } else if (result_cardinality < DEFAULT_MAX_SIZE) { + *result_type = ARRAY_CONTAINER_TYPE_CODE; + bitset_reset_range(bitset->array, min, max+1); + bitset->cardinality = result_cardinality; + return array_container_from_bitset(bitset); + } else { + *result_type = BITSET_CONTAINER_TYPE_CODE; + bitset_reset_range(bitset->array, min, max+1); + bitset->cardinality = result_cardinality; + return bitset; + } + } + case ARRAY_CONTAINER_TYPE_CODE: { + array_container_t *array = (array_container_t *) container; + + int32_t nvals_greater = count_greater(array->array, array->cardinality, max); + int32_t nvals_less = count_less(array->array, array->cardinality - nvals_greater, min); + int32_t result_cardinality = nvals_less + nvals_greater; + + if (result_cardinality == 0) { + return NULL; + } else { + *result_type = ARRAY_CONTAINER_TYPE_CODE; + array_container_remove_range(array, nvals_less, + array->cardinality - result_cardinality); + return array; + } + } + case RUN_CONTAINER_TYPE_CODE: { + run_container_t *run = (run_container_t *) container; + + if (run->n_runs == 0) { + return NULL; + } + if (min <= run_container_minimum(run) && max >= run_container_maximum(run)) { + return NULL; + } + + run_container_remove_range(run, min, max); + + if (run_container_serialized_size_in_bytes(run->n_runs) <= + bitset_container_serialized_size_in_bytes()) { + *result_type = RUN_CONTAINER_TYPE_CODE; + return run; + } else { + *result_type = BITSET_CONTAINER_TYPE_CODE; + return bitset_container_from_run(run); + } + } + case SHARED_CONTAINER_TYPE_CODE: + default: + __builtin_unreachable(); + } +} + +#endif +/* end file include/roaring/containers/containers.h */ +/* begin file include/roaring/roaring_array.h */ +#ifndef INCLUDE_ROARING_ARRAY_H +#define INCLUDE_ROARING_ARRAY_H +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#define MAX_CONTAINERS 65536 + +#define SERIALIZATION_ARRAY_UINT32 1 +#define SERIALIZATION_CONTAINER 2 + +#define ROARING_FLAG_COW UINT8_C(0x1) +#define ROARING_FLAG_FROZEN UINT8_C(0x2) + +enum { + SERIAL_COOKIE_NO_RUNCONTAINER = 12346, + SERIAL_COOKIE = 12347, + FROZEN_COOKIE = 13766, + NO_OFFSET_THRESHOLD = 4 +}; + +/** + * Roaring arrays are array-based key-value pairs having containers as values + * and 16-bit integer keys. A roaring bitmap might be implemented as such. + */ + +// parallel arrays. Element sizes quite different. +// Alternative is array +// of structs. Which would have better +// cache performance through binary searches? + +typedef struct roaring_array_s { + int32_t size; + int32_t allocation_size; + void **containers; + uint16_t *keys; + uint8_t *typecodes; + uint8_t flags; +} roaring_array_t; + +/** + * Create a new roaring array + */ +roaring_array_t *ra_create(void); + +/** + * Initialize an existing roaring array with the specified capacity (in number + * of containers) + */ +bool ra_init_with_capacity(roaring_array_t *new_ra, uint32_t cap); + +/** + * Initialize with zero capacity + */ +void ra_init(roaring_array_t *t); + +/** + * Copies this roaring array, we assume that dest is not initialized + */ +bool ra_copy(const roaring_array_t *source, roaring_array_t *dest, + bool copy_on_write); + +/* + * Shrinks the capacity, returns the number of bytes saved. + */ +int ra_shrink_to_fit(roaring_array_t *ra); + +/** + * Copies this roaring array, we assume that dest is initialized + */ +bool ra_overwrite(const roaring_array_t *source, roaring_array_t *dest, + bool copy_on_write); + +/** + * Frees the memory used by a roaring array + */ +void ra_clear(roaring_array_t *r); + +/** + * Frees the memory used by a roaring array, but does not free the containers + */ +void ra_clear_without_containers(roaring_array_t *r); + +/** + * Frees just the containers + */ +void ra_clear_containers(roaring_array_t *ra); + +/** + * Get the index corresponding to a 16-bit key + */ +static inline int32_t ra_get_index(const roaring_array_t *ra, uint16_t x) { + if ((ra->size == 0) || ra->keys[ra->size - 1] == x) return ra->size - 1; + return binarySearch(ra->keys, (int32_t)ra->size, x); +} + +/** + * Retrieves the container at index i, filling in the typecode + */ +static inline void *ra_get_container_at_index(const roaring_array_t *ra, uint16_t i, + uint8_t *typecode) { + *typecode = ra->typecodes[i]; + return ra->containers[i]; +} + +/** + * Retrieves the key at index i + */ +uint16_t ra_get_key_at_index(const roaring_array_t *ra, uint16_t i); + +/** + * Add a new key-value pair at index i + */ +void ra_insert_new_key_value_at(roaring_array_t *ra, int32_t i, uint16_t key, + void *container, uint8_t typecode); + +/** + * Append a new key-value pair + */ +void ra_append(roaring_array_t *ra, uint16_t s, void *c, uint8_t typecode); + +/** + * Append a new key-value pair to ra, cloning (in COW sense) a value from sa + * at index index + */ +void ra_append_copy(roaring_array_t *ra, const roaring_array_t *sa, + uint16_t index, bool copy_on_write); + +/** + * Append new key-value pairs to ra, cloning (in COW sense) values from sa + * at indexes + * [start_index, end_index) + */ +void ra_append_copy_range(roaring_array_t *ra, const roaring_array_t *sa, + int32_t start_index, int32_t end_index, + bool copy_on_write); + +/** appends from sa to ra, ending with the greatest key that is + * is less or equal stopping_key + */ +void ra_append_copies_until(roaring_array_t *ra, const roaring_array_t *sa, + uint16_t stopping_key, bool copy_on_write); + +/** appends from sa to ra, starting with the smallest key that is + * is strictly greater than before_start + */ + +void ra_append_copies_after(roaring_array_t *ra, const roaring_array_t *sa, + uint16_t before_start, bool copy_on_write); + +/** + * Move the key-value pairs to ra from sa at indexes + * [start_index, end_index), old array should not be freed + * (use ra_clear_without_containers) + **/ +void ra_append_move_range(roaring_array_t *ra, roaring_array_t *sa, + int32_t start_index, int32_t end_index); +/** + * Append new key-value pairs to ra, from sa at indexes + * [start_index, end_index) + */ +void ra_append_range(roaring_array_t *ra, roaring_array_t *sa, + int32_t start_index, int32_t end_index, + bool copy_on_write); + +/** + * Set the container at the corresponding index using the specified + * typecode. + */ +static inline void ra_set_container_at_index(const roaring_array_t *ra, int32_t i, + void *c, uint8_t typecode) { + assert(i < ra->size); + ra->containers[i] = c; + ra->typecodes[i] = typecode; +} + +/** + * If needed, increase the capacity of the array so that it can fit k values + * (at + * least); + */ +bool extend_array(roaring_array_t *ra, int32_t k); + +static inline int32_t ra_get_size(const roaring_array_t *ra) { return ra->size; } + +static inline int32_t ra_advance_until(const roaring_array_t *ra, uint16_t x, + int32_t pos) { + return advanceUntil(ra->keys, pos, ra->size, x); +} + +int32_t ra_advance_until_freeing(roaring_array_t *ra, uint16_t x, int32_t pos); + +void ra_downsize(roaring_array_t *ra, int32_t new_length); + +static inline void ra_replace_key_and_container_at_index(roaring_array_t *ra, + int32_t i, uint16_t key, + void *c, uint8_t typecode) { + assert(i < ra->size); + + ra->keys[i] = key; + ra->containers[i] = c; + ra->typecodes[i] = typecode; +} + +// write set bits to an array +void ra_to_uint32_array(const roaring_array_t *ra, uint32_t *ans); + +bool ra_range_uint32_array(const roaring_array_t *ra, size_t offset, size_t limit, uint32_t *ans); + +/** + * write a bitmap to a buffer. This is meant to be compatible with + * the + * Java and Go versions. Return the size in bytes of the serialized + * output (which should be ra_portable_size_in_bytes(ra)). + */ +size_t ra_portable_serialize(const roaring_array_t *ra, char *buf); + +/** + * read a bitmap from a serialized version. This is meant to be compatible + * with the Java and Go versions. + * maxbytes indicates how many bytes available from buf. + * When the function returns true, roaring_array_t is populated with the data + * and *readbytes indicates how many bytes were read. In all cases, if the function + * returns true, then maxbytes >= *readbytes. + */ +bool ra_portable_deserialize(roaring_array_t *ra, const char *buf, const size_t maxbytes, size_t * readbytes); + +/** + * Quickly checks whether there is a serialized bitmap at the pointer, + * not exceeding size "maxbytes" in bytes. This function does not allocate + * memory dynamically. + * + * This function returns 0 if and only if no valid bitmap is found. + * Otherwise, it returns how many bytes are occupied by the bitmap data. + */ +size_t ra_portable_deserialize_size(const char *buf, const size_t maxbytes); + +/** + * How many bytes are required to serialize this bitmap (meant to be + * compatible + * with Java and Go versions) + */ +size_t ra_portable_size_in_bytes(const roaring_array_t *ra); + +/** + * return true if it contains at least one run container. + */ +bool ra_has_run_container(const roaring_array_t *ra); + +/** + * Size of the header when serializing (meant to be compatible + * with Java and Go versions) + */ +uint32_t ra_portable_header_size(const roaring_array_t *ra); + +/** + * If the container at the index i is share, unshare it (creating a local + * copy if needed). + */ +static inline void ra_unshare_container_at_index(roaring_array_t *ra, + uint16_t i) { + assert(i < ra->size); + ra->containers[i] = + get_writable_copy_if_shared(ra->containers[i], &ra->typecodes[i]); +} + +/** + * remove at index i, sliding over all entries after i + */ +void ra_remove_at_index(roaring_array_t *ra, int32_t i); + + +/** +* clears all containers, sets the size at 0 and shrinks the memory usage. +*/ +void ra_reset(roaring_array_t *ra); + +/** + * remove at index i, sliding over all entries after i. Free removed container. + */ +void ra_remove_at_index_and_free(roaring_array_t *ra, int32_t i); + +/** + * remove a chunk of indices, sliding over entries after it + */ +// void ra_remove_index_range(roaring_array_t *ra, int32_t begin, int32_t end); + +// used in inplace andNot only, to slide left the containers from +// the mutated RoaringBitmap that are after the largest container of +// the argument RoaringBitmap. It is followed by a call to resize. +// +void ra_copy_range(roaring_array_t *ra, uint32_t begin, uint32_t end, + uint32_t new_begin); + +/** + * Shifts rightmost $count containers to the left (distance < 0) or + * to the right (distance > 0). + * Allocates memory if necessary. + * This function doesn't free or create new containers. + * Caller is responsible for that. + */ +void ra_shift_tail(roaring_array_t *ra, int32_t count, int32_t distance); + +#ifdef __cplusplus +} +#endif + +#endif +/* end file include/roaring/roaring_array.h */ +/* begin file include/roaring/roaring.h */ +/* +An implementation of Roaring Bitmaps in C. +*/ + +#ifndef ROARING_H +#define ROARING_H +#ifdef __cplusplus +extern "C" { +#endif + +#include + +typedef struct roaring_bitmap_s { + roaring_array_t high_low_container; +} roaring_bitmap_t; + +/** + * Creates a new bitmap (initially empty) + */ +roaring_bitmap_t *roaring_bitmap_create(void); + +/** + * Add all the values between min (included) and max (excluded) that are at a + * distance k*step from min. +*/ +roaring_bitmap_t *roaring_bitmap_from_range(uint64_t min, uint64_t max, + uint32_t step); + +/** + * Creates a new bitmap (initially empty) with a provided + * container-storage capacity (it is a performance hint). + */ +roaring_bitmap_t *roaring_bitmap_create_with_capacity(uint32_t cap); + +/** + * Creates a new bitmap from a pointer of uint32_t integers + */ +roaring_bitmap_t *roaring_bitmap_of_ptr(size_t n_args, const uint32_t *vals); + +/* + * Whether you want to use copy-on-write. + * Saves memory and avoids copies but needs more care in a threaded context. + * Most users should ignore this flag. + * Note: if you do turn this flag to 'true', enabling COW, + * then ensure that you do so for all of your bitmaps since + * interactions between bitmaps with and without COW is unsafe. + */ +static inline bool roaring_bitmap_get_copy_on_write(const roaring_bitmap_t* r) { + return r->high_low_container.flags & ROARING_FLAG_COW; +} +static inline void roaring_bitmap_set_copy_on_write(roaring_bitmap_t* r, bool cow) { + if (cow) { + r->high_low_container.flags |= ROARING_FLAG_COW; + } else { + r->high_low_container.flags &= ~ROARING_FLAG_COW; + } +} + +/** + * Describe the inner structure of the bitmap. + */ +void roaring_bitmap_printf_describe(const roaring_bitmap_t *ra); + +/** + * Creates a new bitmap from a list of uint32_t integers + */ +roaring_bitmap_t *roaring_bitmap_of(size_t n, ...); + +/** + * Copies a bitmap. This does memory allocation. The caller is responsible for + * memory management. + * + */ +roaring_bitmap_t *roaring_bitmap_copy(const roaring_bitmap_t *r); + + +/** + * Copies a bitmap from src to dest. It is assumed that the pointer dest + * is to an already allocated bitmap. The content of the dest bitmap is + * freed/deleted. + * + * It might be preferable and simpler to call roaring_bitmap_copy except + * that roaring_bitmap_overwrite can save on memory allocations. + * + */ +bool roaring_bitmap_overwrite(roaring_bitmap_t *dest, + const roaring_bitmap_t *src); + +/** + * Print the content of the bitmap. + */ +void roaring_bitmap_printf(const roaring_bitmap_t *ra); + +/** + * Computes the intersection between two bitmaps and returns new bitmap. The + * caller is + * responsible for memory management. + * + */ +roaring_bitmap_t *roaring_bitmap_and(const roaring_bitmap_t *x1, + const roaring_bitmap_t *x2); + +/** + * Computes the size of the intersection between two bitmaps. + * + */ +uint64_t roaring_bitmap_and_cardinality(const roaring_bitmap_t *x1, + const roaring_bitmap_t *x2); + + +/** + * Check whether two bitmaps intersect. + * + */ +bool roaring_bitmap_intersect(const roaring_bitmap_t *x1, + const roaring_bitmap_t *x2); + +/** + * Computes the Jaccard index between two bitmaps. (Also known as the Tanimoto + * distance, + * or the Jaccard similarity coefficient) + * + * The Jaccard index is undefined if both bitmaps are empty. + * + */ +double roaring_bitmap_jaccard_index(const roaring_bitmap_t *x1, + const roaring_bitmap_t *x2); + +/** + * Computes the size of the union between two bitmaps. + * + */ +uint64_t roaring_bitmap_or_cardinality(const roaring_bitmap_t *x1, + const roaring_bitmap_t *x2); + +/** + * Computes the size of the difference (andnot) between two bitmaps. + * + */ +uint64_t roaring_bitmap_andnot_cardinality(const roaring_bitmap_t *x1, + const roaring_bitmap_t *x2); + +/** + * Computes the size of the symmetric difference (andnot) between two bitmaps. + * + */ +uint64_t roaring_bitmap_xor_cardinality(const roaring_bitmap_t *x1, + const roaring_bitmap_t *x2); + +/** + * Inplace version modifies x1, x1 == x2 is allowed + */ +void roaring_bitmap_and_inplace(roaring_bitmap_t *x1, + const roaring_bitmap_t *x2); + +/** + * Computes the union between two bitmaps and returns new bitmap. The caller is + * responsible for memory management. + */ +roaring_bitmap_t *roaring_bitmap_or(const roaring_bitmap_t *x1, + const roaring_bitmap_t *x2); + +/** + * Inplace version of roaring_bitmap_or, modifies x1. TDOO: decide whether x1 == + *x2 ok + * + */ +void roaring_bitmap_or_inplace(roaring_bitmap_t *x1, + const roaring_bitmap_t *x2); + +/** + * Compute the union of 'number' bitmaps. See also roaring_bitmap_or_many_heap. + * Caller is responsible for freeing the + * result. + * + */ +roaring_bitmap_t *roaring_bitmap_or_many(size_t number, + const roaring_bitmap_t **x); + +/** + * Compute the union of 'number' bitmaps using a heap. This can + * sometimes be faster than roaring_bitmap_or_many which uses + * a naive algorithm. Caller is responsible for freeing the + * result. + * + */ +roaring_bitmap_t *roaring_bitmap_or_many_heap(uint32_t number, + const roaring_bitmap_t **x); + +/** + * Computes the symmetric difference (xor) between two bitmaps + * and returns new bitmap. The caller is responsible for memory management. + */ +roaring_bitmap_t *roaring_bitmap_xor(const roaring_bitmap_t *x1, + const roaring_bitmap_t *x2); + +/** + * Inplace version of roaring_bitmap_xor, modifies x1. x1 != x2. + * + */ +void roaring_bitmap_xor_inplace(roaring_bitmap_t *x1, + const roaring_bitmap_t *x2); + +/** + * Compute the xor of 'number' bitmaps. + * Caller is responsible for freeing the + * result. + * + */ +roaring_bitmap_t *roaring_bitmap_xor_many(size_t number, + const roaring_bitmap_t **x); + +/** + * Computes the difference (andnot) between two bitmaps + * and returns new bitmap. The caller is responsible for memory management. + */ +roaring_bitmap_t *roaring_bitmap_andnot(const roaring_bitmap_t *x1, + const roaring_bitmap_t *x2); + +/** + * Inplace version of roaring_bitmap_andnot, modifies x1. x1 != x2. + * + */ +void roaring_bitmap_andnot_inplace(roaring_bitmap_t *x1, + const roaring_bitmap_t *x2); + +/** + * TODO: consider implementing: + * Compute the xor of 'number' bitmaps using a heap. This can + * sometimes be faster than roaring_bitmap_xor_many which uses + * a naive algorithm. Caller is responsible for freeing the + * result. + * + * roaring_bitmap_t *roaring_bitmap_xor_many_heap(uint32_t number, + * const roaring_bitmap_t **x); + */ + +/** + * Frees the memory. + */ +void roaring_bitmap_free(const roaring_bitmap_t *r); + +/** + * Add value n_args from pointer vals, faster than repeatedly calling + * roaring_bitmap_add + * + */ +void roaring_bitmap_add_many(roaring_bitmap_t *r, size_t n_args, + const uint32_t *vals); + +/** + * Add value x + * + */ +void roaring_bitmap_add(roaring_bitmap_t *r, uint32_t x); + +/** + * Add value x + * Returns true if a new value was added, false if the value was already existing. + */ +bool roaring_bitmap_add_checked(roaring_bitmap_t *r, uint32_t x); + +/** + * Add all values in range [min, max] + */ +void roaring_bitmap_add_range_closed(roaring_bitmap_t *ra, uint32_t min, uint32_t max); + +/** + * Add all values in range [min, max) + */ +static inline void roaring_bitmap_add_range(roaring_bitmap_t *ra, uint64_t min, uint64_t max) { + if(max == min) return; + roaring_bitmap_add_range_closed(ra, (uint32_t)min, (uint32_t)(max - 1)); +} + +/** + * Remove value x + * + */ +void roaring_bitmap_remove(roaring_bitmap_t *r, uint32_t x); + +/** Remove all values in range [min, max] */ +void roaring_bitmap_remove_range_closed(roaring_bitmap_t *ra, uint32_t min, uint32_t max); + +/** Remove all values in range [min, max) */ +static inline void roaring_bitmap_remove_range(roaring_bitmap_t *ra, uint64_t min, uint64_t max) { + if(max == min) return; + roaring_bitmap_remove_range_closed(ra, (uint32_t)min, (uint32_t)(max - 1)); +} + +/** Remove multiple values */ +void roaring_bitmap_remove_many(roaring_bitmap_t *r, size_t n_args, + const uint32_t *vals); + +/** + * Remove value x + * Returns true if a new value was removed, false if the value was not existing. + */ +bool roaring_bitmap_remove_checked(roaring_bitmap_t *r, uint32_t x); + +/** + * Check if value x is present + */ +static inline bool roaring_bitmap_contains(const roaring_bitmap_t *r, uint32_t val) { + const uint16_t hb = val >> 16; + /* + * the next function call involves a binary search and lots of branching. + */ + int32_t i = ra_get_index(&r->high_low_container, hb); + if (i < 0) return false; + + uint8_t typecode; + // next call ought to be cheap + void *container = + ra_get_container_at_index(&r->high_low_container, i, &typecode); + // rest might be a tad expensive, possibly involving another round of binary search + return container_contains(container, val & 0xFFFF, typecode); +} + +/** + * Check whether a range of values from range_start (included) to range_end (excluded) is present + */ +bool roaring_bitmap_contains_range(const roaring_bitmap_t *r, uint64_t range_start, uint64_t range_end); + +/** + * Get the cardinality of the bitmap (number of elements). + */ +uint64_t roaring_bitmap_get_cardinality(const roaring_bitmap_t *ra); + +/** + * Returns the number of elements in the range [range_start, range_end). + */ +uint64_t roaring_bitmap_range_cardinality(const roaring_bitmap_t *ra, + uint64_t range_start, uint64_t range_end); + +/** +* Returns true if the bitmap is empty (cardinality is zero). +*/ +bool roaring_bitmap_is_empty(const roaring_bitmap_t *ra); + + +/** +* Empties the bitmap +*/ +void roaring_bitmap_clear(roaring_bitmap_t *ra); + +/** + * Convert the bitmap to an array. Write the output to "ans", + * caller is responsible to ensure that there is enough memory + * allocated + * (e.g., ans = malloc(roaring_bitmap_get_cardinality(mybitmap) + * * sizeof(uint32_t)) + */ +void roaring_bitmap_to_uint32_array(const roaring_bitmap_t *ra, uint32_t *ans); + + +/** + * Convert the bitmap to an array from "offset" by "limit". Write the output to "ans". + * so, you can get data in paging. + * caller is responsible to ensure that there is enough memory + * allocated + * (e.g., ans = malloc(roaring_bitmap_get_cardinality(limit) + * * sizeof(uint32_t)) + * Return false in case of failure (e.g., insufficient memory) + */ +bool roaring_bitmap_range_uint32_array(const roaring_bitmap_t *ra, size_t offset, size_t limit, uint32_t *ans); + +/** + * Remove run-length encoding even when it is more space efficient + * return whether a change was applied + */ +bool roaring_bitmap_remove_run_compression(roaring_bitmap_t *r); + +/** convert array and bitmap containers to run containers when it is more + * efficient; + * also convert from run containers when more space efficient. Returns + * true if the result has at least one run container. + * Additional savings might be possible by calling shrinkToFit(). + */ +bool roaring_bitmap_run_optimize(roaring_bitmap_t *r); + +/** + * If needed, reallocate memory to shrink the memory usage. Returns + * the number of bytes saved. +*/ +size_t roaring_bitmap_shrink_to_fit(roaring_bitmap_t *r); + +/** +* write the bitmap to an output pointer, this output buffer should refer to +* at least roaring_bitmap_size_in_bytes(ra) allocated bytes. +* +* see roaring_bitmap_portable_serialize if you want a format that's compatible +* with Java and Go implementations +* +* this format has the benefit of being sometimes more space efficient than +* roaring_bitmap_portable_serialize +* e.g., when the data is sparse. +* +* Returns how many bytes were written which should be +* roaring_bitmap_size_in_bytes(ra). +*/ +size_t roaring_bitmap_serialize(const roaring_bitmap_t *ra, char *buf); + +/** use with roaring_bitmap_serialize +* see roaring_bitmap_portable_deserialize if you want a format that's +* compatible with Java and Go implementations +*/ +roaring_bitmap_t *roaring_bitmap_deserialize(const void *buf); + +/** + * How many bytes are required to serialize this bitmap (NOT compatible + * with Java and Go versions) + */ +size_t roaring_bitmap_size_in_bytes(const roaring_bitmap_t *ra); + +/** + * read a bitmap from a serialized version. This is meant to be compatible with + * the Java and Go versions. See format specification at + * https://github.com/RoaringBitmap/RoaringFormatSpec + * In case of failure, a null pointer is returned. + * This function is unsafe in the sense that if there is no valid serialized + * bitmap at the pointer, then many bytes could be read, possibly causing a buffer + * overflow. For a safer approach, + * call roaring_bitmap_portable_deserialize_safe. + */ +roaring_bitmap_t *roaring_bitmap_portable_deserialize(const char *buf); + +/** + * read a bitmap from a serialized version in a safe manner (reading up to maxbytes). + * This is meant to be compatible with + * the Java and Go versions. See format specification at + * https://github.com/RoaringBitmap/RoaringFormatSpec + * In case of failure, a null pointer is returned. + */ +roaring_bitmap_t *roaring_bitmap_portable_deserialize_safe(const char *buf, size_t maxbytes); + +/** + * Check how many bytes would be read (up to maxbytes) at this pointer if there + * is a bitmap, returns zero if there is no valid bitmap. + * This is meant to be compatible with + * the Java and Go versions. See format specification at + * https://github.com/RoaringBitmap/RoaringFormatSpec + */ +size_t roaring_bitmap_portable_deserialize_size(const char *buf, size_t maxbytes); + + +/** + * How many bytes are required to serialize this bitmap (meant to be compatible + * with Java and Go versions). See format specification at + * https://github.com/RoaringBitmap/RoaringFormatSpec + */ +size_t roaring_bitmap_portable_size_in_bytes(const roaring_bitmap_t *ra); + +/** + * write a bitmap to a char buffer. The output buffer should refer to at least + * roaring_bitmap_portable_size_in_bytes(ra) bytes of allocated memory. + * This is meant to be compatible with + * the + * Java and Go versions. Returns how many bytes were written which should be + * roaring_bitmap_portable_size_in_bytes(ra). See format specification at + * https://github.com/RoaringBitmap/RoaringFormatSpec + */ +size_t roaring_bitmap_portable_serialize(const roaring_bitmap_t *ra, char *buf); + +/* + * "Frozen" serialization format imitates memory layout of roaring_bitmap_t. + * Deserialized bitmap is a constant view of the underlying buffer. + * This significantly reduces amount of allocations and copying required during + * deserialization. + * It can be used with memory mapped files. + * Example can be found in benchmarks/frozen_benchmark.c + * + * [#####] const roaring_bitmap_t * + * | | | + * +----+ | +-+ + * | | | + * [#####################################] underlying buffer + * + * Note that because frozen serialization format imitates C memory layout + * of roaring_bitmap_t, it is not fixed. It is different on big/little endian + * platforms and can be changed in future. + */ + +/** + * Returns number of bytes required to serialize bitmap using frozen format. + */ +size_t roaring_bitmap_frozen_size_in_bytes(const roaring_bitmap_t *ra); + +/** + * Serializes bitmap using frozen format. + * Buffer size must be at least roaring_bitmap_frozen_size_in_bytes(). + */ +void roaring_bitmap_frozen_serialize(const roaring_bitmap_t *ra, char *buf); + +/** + * Creates constant bitmap that is a view of a given buffer. + * Buffer must contain data previously written by roaring_bitmap_frozen_serialize(), + * and additionally its beginning must be aligned by 32 bytes. + * Length must be equal exactly to roaring_bitmap_frozen_size_in_bytes(). + * + * On error, NULL is returned. + * + * Bitmap returned by this function can be used in all readonly contexts. + * Bitmap must be freed as usual, by calling roaring_bitmap_free(). + * Underlying buffer must not be freed or modified while it backs any bitmaps. + */ +const roaring_bitmap_t *roaring_bitmap_frozen_view(const char *buf, size_t length); + + +/** + * Iterate over the bitmap elements. The function iterator is called once for + * all the values with ptr (can be NULL) as the second parameter of each call. + * + * roaring_iterator is simply a pointer to a function that returns bool + * (true means that the iteration should continue while false means that it + * should stop), + * and takes (uint32_t,void*) as inputs. + * + * Returns true if the roaring_iterator returned true throughout (so that + * all data points were necessarily visited). + */ +bool roaring_iterate(const roaring_bitmap_t *ra, roaring_iterator iterator, + void *ptr); + +bool roaring_iterate64(const roaring_bitmap_t *ra, roaring_iterator64 iterator, + uint64_t high_bits, void *ptr); + +/** + * Return true if the two bitmaps contain the same elements. + */ +bool roaring_bitmap_equals(const roaring_bitmap_t *ra1, + const roaring_bitmap_t *ra2); + +/** + * Return true if all the elements of ra1 are also in ra2. + */ +bool roaring_bitmap_is_subset(const roaring_bitmap_t *ra1, + const roaring_bitmap_t *ra2); + +/** + * Return true if all the elements of ra1 are also in ra2 and ra2 is strictly + * greater + * than ra1. + */ +bool roaring_bitmap_is_strict_subset(const roaring_bitmap_t *ra1, + const roaring_bitmap_t *ra2); + +/** + * (For expert users who seek high performance.) + * + * Computes the union between two bitmaps and returns new bitmap. The caller is + * responsible for memory management. + * + * The lazy version defers some computations such as the maintenance of the + * cardinality counts. Thus you need + * to call roaring_bitmap_repair_after_lazy after executing "lazy" computations. + * It is safe to repeatedly call roaring_bitmap_lazy_or_inplace on the result. + * The bitsetconversion conversion is a flag which determines + * whether container-container operations force a bitset conversion. + **/ +roaring_bitmap_t *roaring_bitmap_lazy_or(const roaring_bitmap_t *x1, + const roaring_bitmap_t *x2, + const bool bitsetconversion); + +/** + * (For expert users who seek high performance.) + * Inplace version of roaring_bitmap_lazy_or, modifies x1 + * The bitsetconversion conversion is a flag which determines + * whether container-container operations force a bitset conversion. + */ +void roaring_bitmap_lazy_or_inplace(roaring_bitmap_t *x1, + const roaring_bitmap_t *x2, + const bool bitsetconversion); + +/** + * (For expert users who seek high performance.) + * + * Execute maintenance operations on a bitmap created from + * roaring_bitmap_lazy_or + * or modified with roaring_bitmap_lazy_or_inplace. + */ +void roaring_bitmap_repair_after_lazy(roaring_bitmap_t *x1); + +/** + * Computes the symmetric difference between two bitmaps and returns new bitmap. + *The caller is + * responsible for memory management. + * + * The lazy version defers some computations such as the maintenance of the + * cardinality counts. Thus you need + * to call roaring_bitmap_repair_after_lazy after executing "lazy" computations. + * It is safe to repeatedly call roaring_bitmap_lazy_xor_inplace on the result. + * + */ +roaring_bitmap_t *roaring_bitmap_lazy_xor(const roaring_bitmap_t *x1, + const roaring_bitmap_t *x2); + +/** + * (For expert users who seek high performance.) + * Inplace version of roaring_bitmap_lazy_xor, modifies x1. x1 != x2 + * + */ +void roaring_bitmap_lazy_xor_inplace(roaring_bitmap_t *x1, + const roaring_bitmap_t *x2); + +/** + * compute the negation of the roaring bitmap within a specified + * interval: [range_start, range_end). The number of negated values is + * range_end - range_start. + * Areas outside the range are passed through unchanged. + */ + +roaring_bitmap_t *roaring_bitmap_flip(const roaring_bitmap_t *x1, + uint64_t range_start, uint64_t range_end); + +/** + * compute (in place) the negation of the roaring bitmap within a specified + * interval: [range_start, range_end). The number of negated values is + * range_end - range_start. + * Areas outside the range are passed through unchanged. + */ + +void roaring_bitmap_flip_inplace(roaring_bitmap_t *x1, uint64_t range_start, + uint64_t range_end); + +/** + * Selects the element at index 'rank' where the smallest element is at index 0. + * If the size of the roaring bitmap is strictly greater than rank, then this + function returns true and sets element to the element of given rank. + Otherwise, it returns false. + */ +bool roaring_bitmap_select(const roaring_bitmap_t *ra, uint32_t rank, + uint32_t *element); +/** +* roaring_bitmap_rank returns the number of integers that are smaller or equal +* to x. Thus if x is the first element, this function will return 1. If +* x is smaller than the smallest element, this function will return 0. +* +* The indexing convention differs between roaring_bitmap_select and +* roaring_bitmap_rank: roaring_bitmap_select refers to the smallest value +* as having index 0, whereas roaring_bitmap_rank returns 1 when ranking +* the smallest value. +*/ +uint64_t roaring_bitmap_rank(const roaring_bitmap_t *bm, uint32_t x); + +/** +* roaring_bitmap_smallest returns the smallest value in the set. +* Returns UINT32_MAX if the set is empty. +*/ +uint32_t roaring_bitmap_minimum(const roaring_bitmap_t *bm); + +/** +* roaring_bitmap_smallest returns the greatest value in the set. +* Returns 0 if the set is empty. +*/ +uint32_t roaring_bitmap_maximum(const roaring_bitmap_t *bm); + +/** +* (For advanced users.) +* Collect statistics about the bitmap, see roaring_types.h for +* a description of roaring_statistics_t +*/ +void roaring_bitmap_statistics(const roaring_bitmap_t *ra, + roaring_statistics_t *stat); + +/********************* +* What follows is code use to iterate through values in a roaring bitmap + +roaring_bitmap_t *ra =... +roaring_uint32_iterator_t i; +roaring_create_iterator(ra, &i); +while(i.has_value) { + printf("value = %d\n", i.current_value); + roaring_advance_uint32_iterator(&i); +} + +Obviously, if you modify the underlying bitmap, the iterator +becomes invalid. So don't. +*/ + +typedef struct roaring_uint32_iterator_s { + const roaring_bitmap_t *parent; // owner + int32_t container_index; // point to the current container index + int32_t in_container_index; // for bitset and array container, this is out + // index + int32_t run_index; // for run container, this points at the run + + uint32_t current_value; + bool has_value; + + const void + *container; // should be: + // parent->high_low_container.containers[container_index]; + uint8_t typecode; // should be: + // parent->high_low_container.typecodes[container_index]; + uint32_t highbits; // should be: + // parent->high_low_container.keys[container_index]) << + // 16; + +} roaring_uint32_iterator_t; + +/** +* Initialize an iterator object that can be used to iterate through the +* values. If there is a value, then this iterator points to the first value +* and it->has_value is true. The value is in it->current_value. +*/ +void roaring_init_iterator(const roaring_bitmap_t *ra, + roaring_uint32_iterator_t *newit); + +/** +* Initialize an iterator object that can be used to iterate through the +* values. If there is a value, then this iterator points to the last value +* and it->has_value is true. The value is in it->current_value. +*/ +void roaring_init_iterator_last(const roaring_bitmap_t *ra, + roaring_uint32_iterator_t *newit); + +/** +* Create an iterator object that can be used to iterate through the +* values. Caller is responsible for calling roaring_free_iterator. +* The iterator is initialized. If there is a value, then this iterator +* points to the first value and it->has_value is true. +* The value is in it->current_value. +* +* This function calls roaring_init_iterator. +*/ +roaring_uint32_iterator_t *roaring_create_iterator(const roaring_bitmap_t *ra); + +/** +* Advance the iterator. If there is a new value, then it->has_value is true. +* The new value is in it->current_value. Values are traversed in increasing +* orders. For convenience, returns it->has_value. +*/ +bool roaring_advance_uint32_iterator(roaring_uint32_iterator_t *it); + +/** +* Decrement the iterator. If there is a new value, then it->has_value is true. +* The new value is in it->current_value. Values are traversed in decreasing +* orders. For convenience, returns it->has_value. +*/ +bool roaring_previous_uint32_iterator(roaring_uint32_iterator_t *it); + +/** +* Move the iterator to the first value >= val. If there is a such a value, then it->has_value is true. +* The new value is in it->current_value. For convenience, returns it->has_value. +*/ +bool roaring_move_uint32_iterator_equalorlarger(roaring_uint32_iterator_t *it, uint32_t val) ; +/** +* Creates a copy of an iterator. +* Caller must free it. +*/ +roaring_uint32_iterator_t *roaring_copy_uint32_iterator( + const roaring_uint32_iterator_t *it); + +/** +* Free memory following roaring_create_iterator +*/ +void roaring_free_uint32_iterator(roaring_uint32_iterator_t *it); + +/* + * Reads next ${count} values from iterator into user-supplied ${buf}. + * Returns the number of read elements. + * This number can be smaller than ${count}, which means that iterator is drained. + * + * This function satisfies semantics of iteration and can be used together with + * other iterator functions. + * - first value is copied from ${it}->current_value + * - after function returns, iterator is positioned at the next element + */ +uint32_t roaring_read_uint32_iterator(roaring_uint32_iterator_t *it, uint32_t* buf, uint32_t count); + +#ifdef __cplusplus +} +#endif + +#endif +/* end file include/roaring/roaring.h */ diff --git a/contrib/meson.build b/contrib/meson.build index 691bcd0d..1b295c8c 100644 --- a/contrib/meson.build +++ b/contrib/meson.build @@ -1 +1,2 @@ +subdir('eggbitset') subdir('elfparser') From ff41633abc1b81b0341f4ec17d6286602d680a05 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 25 May 2023 15:13:45 -0700 Subject: [PATCH 0262/1030] libsysprof-analyze: use EggBitset instead of GTK This uses the newly brought in EggBitset instead of GtkBitset so that we can drop our GTK dependency in the libsysprof-analyze library. --- src/libsysprof-analyze/meson.build | 2 +- .../sysprof-address-layout.c | 16 +- .../sysprof-document-bitset-index-private.h | 4 +- .../sysprof-document-bitset-index.c | 14 +- .../sysprof-document-private.h | 4 +- .../sysprof-document-symbols.c | 8 +- src/libsysprof-analyze/sysprof-document.c | 138 +++++++++--------- 7 files changed, 93 insertions(+), 93 deletions(-) diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build index d126823e..1fcf1972 100644 --- a/src/libsysprof-analyze/meson.build +++ b/src/libsysprof-analyze/meson.build @@ -84,8 +84,8 @@ libsysprof_analyze_public_headers = [ libsysprof_analyze_deps = [ dependency('gio-2.0', version: glib_req_version), - dependency('gtk4', version: gtk_req_version), + libeggbitset_static_dep, libelfparser_static_dep, libsysprof_capture_dep, ] diff --git a/src/libsysprof-analyze/sysprof-address-layout.c b/src/libsysprof-analyze/sysprof-address-layout.c index 1e3784fb..ff92fe26 100644 --- a/src/libsysprof-analyze/sysprof-address-layout.c +++ b/src/libsysprof-analyze/sysprof-address-layout.c @@ -21,7 +21,7 @@ #include "config.h" #include -#include +#include #include "sysprof-address-layout-private.h" @@ -146,10 +146,10 @@ mmaps_overlap (SysprofDocumentMmap *a, return FALSE; } -static GtkBitset * +static EggBitset * find_duplicates (GPtrArray *sorted) { - GtkBitset *bitset = gtk_bitset_new_empty (); + EggBitset *bitset = egg_bitset_new_empty (); if (sorted->len == 0) return bitset; @@ -165,7 +165,7 @@ find_duplicates (GPtrArray *sorted) * predictable bsearch results. */ if (mmaps_overlap (map, next)) - gtk_bitset_add (bitset, i); + egg_bitset_add (bitset, i); } return bitset; @@ -197,8 +197,8 @@ sysprof_address_layout_lookup (SysprofAddressLayout *self, if (self->mmaps_dirty) { - g_autoptr(GtkBitset) dups = NULL; - GtkBitsetIter iter; + g_autoptr(EggBitset) dups = NULL; + EggBitsetIter iter; guint old_len = self->mmaps->len; guint i; @@ -207,11 +207,11 @@ sysprof_address_layout_lookup (SysprofAddressLayout *self, g_ptr_array_sort (self->mmaps, compare_mmaps); dups = find_duplicates (self->mmaps); - if (gtk_bitset_iter_init_last (&iter, dups, &i)) + if (egg_bitset_iter_init_last (&iter, dups, &i)) { do g_ptr_array_remove_index (self->mmaps, i); - while (gtk_bitset_iter_previous (&iter, &i)); + while (egg_bitset_iter_previous (&iter, &i)); } g_list_model_items_changed (G_LIST_MODEL (self), 0, old_len, self->mmaps->len); diff --git a/src/libsysprof-analyze/sysprof-document-bitset-index-private.h b/src/libsysprof-analyze/sysprof-document-bitset-index-private.h index b541828c..0432f441 100644 --- a/src/libsysprof-analyze/sysprof-document-bitset-index-private.h +++ b/src/libsysprof-analyze/sysprof-document-bitset-index-private.h @@ -21,7 +21,7 @@ #pragma once #include -#include +#include G_BEGIN_DECLS @@ -30,6 +30,6 @@ G_BEGIN_DECLS G_DECLARE_FINAL_TYPE (SysprofDocumentBitsetIndex, sysprof_document_bitset_index, SYSPROF, DOCUMENT_BITSET_INDEX, GObject) GListModel *_sysprof_document_bitset_index_new (GListModel *model, - GtkBitset *bitset); + EggBitset *bitset); G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-document-bitset-index.c b/src/libsysprof-analyze/sysprof-document-bitset-index.c index 8f4b9496..a2386853 100644 --- a/src/libsysprof-analyze/sysprof-document-bitset-index.c +++ b/src/libsysprof-analyze/sysprof-document-bitset-index.c @@ -26,7 +26,7 @@ struct _SysprofDocumentBitsetIndex { GObject parent_instance; GListModel *model; - GtkBitset *bitset; + EggBitset *bitset; }; static GType @@ -46,7 +46,7 @@ sysprof_document_bitset_index_get_n_items (GListModel *model) SysprofDocumentBitsetIndex *self = SYSPROF_DOCUMENT_BITSET_INDEX (model); if (self->bitset != NULL) - return gtk_bitset_get_size (self->bitset); + return egg_bitset_get_size (self->bitset); return 0; } @@ -60,11 +60,11 @@ sysprof_document_bitset_index_get_item (GListModel *model, if (self->model == NULL || self->bitset == NULL) return NULL; - if (position >= gtk_bitset_get_size (self->bitset)) + if (position >= egg_bitset_get_size (self->bitset)) return NULL; return g_list_model_get_item (self->model, - gtk_bitset_get_nth (self->bitset, position)); + egg_bitset_get_nth (self->bitset, position)); } static void @@ -83,7 +83,7 @@ sysprof_document_bitset_index_dispose (GObject *object) { SysprofDocumentBitsetIndex *self = (SysprofDocumentBitsetIndex *)object; - g_clear_pointer (&self->bitset, gtk_bitset_unref); + g_clear_pointer (&self->bitset, egg_bitset_unref); g_clear_object (&self->model); G_OBJECT_CLASS (sysprof_document_bitset_index_parent_class)->dispose (object); @@ -104,7 +104,7 @@ sysprof_document_bitset_index_init (SysprofDocumentBitsetIndex *self) GListModel * _sysprof_document_bitset_index_new (GListModel *model, - GtkBitset *bitset) + EggBitset *bitset) { SysprofDocumentBitsetIndex *self; @@ -113,7 +113,7 @@ _sysprof_document_bitset_index_new (GListModel *model, self = g_object_new (SYSPROF_TYPE_DOCUMENT_BITSET_INDEX, NULL); self->model = g_object_ref (model); - self->bitset = gtk_bitset_ref (bitset); + self->bitset = egg_bitset_ref (bitset); return G_LIST_MODEL (self); } diff --git a/src/libsysprof-analyze/sysprof-document-private.h b/src/libsysprof-analyze/sysprof-document-private.h index 4e2dddd4..2f54d047 100644 --- a/src/libsysprof-analyze/sysprof-document-private.h +++ b/src/libsysprof-analyze/sysprof-document-private.h @@ -20,7 +20,7 @@ #pragma once -#include +#include #include "sysprof-document.h" #include "sysprof-symbolizer.h" @@ -55,7 +55,7 @@ gboolean _sysprof_document_symbolize_finish (SysprofDocument *self, gboolean _sysprof_document_is_native (SysprofDocument *self); GRefString *_sysprof_document_ref_string (SysprofDocument *self, const char *name); -GtkBitset *_sysprof_document_traceables (SysprofDocument *self); +EggBitset *_sysprof_document_traceables (SysprofDocument *self); SysprofSymbol *_sysprof_document_process_symbol (SysprofDocument *self, int pid); SysprofSymbol *_sysprof_document_kernel_symbol (SysprofDocument *self); diff --git a/src/libsysprof-analyze/sysprof-document-symbols.c b/src/libsysprof-analyze/sysprof-document-symbols.c index 688c6c7c..b18d58e5 100644 --- a/src/libsysprof-analyze/sysprof-document-symbols.c +++ b/src/libsysprof-analyze/sysprof-document-symbols.c @@ -149,8 +149,8 @@ sysprof_document_symbols_worker (GTask *task, { "- - Guest User - -", SYSPROF_ADDRESS_CONTEXT_GUEST_USER }, }; Symbolize *state = task_data; - GtkBitsetIter iter; - GtkBitset *bitset; + EggBitsetIter iter; + EggBitset *bitset; GListModel *model; guint i; @@ -182,7 +182,7 @@ sysprof_document_symbols_worker (GTask *task, /* Walk through the available traceables which need symbols extracted */ if (!SYSPROF_IS_NO_SYMBOLIZER (state->symbolizer) && - gtk_bitset_iter_init_first (&iter, bitset, &i)) + egg_bitset_iter_init_first (&iter, bitset, &i)) { do { @@ -196,7 +196,7 @@ sysprof_document_symbols_worker (GTask *task, traceable, state->symbolizer); } - while (gtk_bitset_iter_next (&iter, &i)); + while (egg_bitset_iter_next (&iter, &i)); } g_task_return_pointer (task, diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index 553e2203..27f42a96 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -62,16 +62,16 @@ struct _SysprofDocument SysprofStrings *strings; - GtkBitset *file_chunks; - GtkBitset *samples; - GtkBitset *traceables; - GtkBitset *processes; - GtkBitset *mmaps; - GtkBitset *overlays; - GtkBitset *pids; - GtkBitset *jitmaps; - GtkBitset *ctrdefs; - GtkBitset *ctrsets; + EggBitset *file_chunks; + EggBitset *samples; + EggBitset *traceables; + EggBitset *processes; + EggBitset *mmaps; + EggBitset *overlays; + EggBitset *pids; + EggBitset *jitmaps; + EggBitset *ctrdefs; + EggBitset *ctrsets; GHashTable *files_first_position; GHashTable *pid_to_process_info; @@ -143,7 +143,7 @@ list_model_iface_init (GListModelInterface *iface) G_DEFINE_FINAL_TYPE_WITH_CODE (SysprofDocument, sysprof_document, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init)) -GtkBitset * +EggBitset * _sysprof_document_traceables (SysprofDocument *self) { g_return_val_if_fail (SYSPROF_IS_DOCUMENT (self), NULL); @@ -210,16 +210,16 @@ sysprof_document_finalize (GObject *object) g_clear_pointer (&self->mapped_file, g_mapped_file_unref); g_clear_pointer (&self->frames, g_array_unref); - g_clear_pointer (&self->ctrdefs, gtk_bitset_unref); - g_clear_pointer (&self->ctrsets, gtk_bitset_unref); - g_clear_pointer (&self->file_chunks, gtk_bitset_unref); - g_clear_pointer (&self->jitmaps, gtk_bitset_unref); - g_clear_pointer (&self->mmaps, gtk_bitset_unref); - g_clear_pointer (&self->overlays, gtk_bitset_unref); - g_clear_pointer (&self->pids, gtk_bitset_unref); - g_clear_pointer (&self->processes, gtk_bitset_unref); - g_clear_pointer (&self->samples, gtk_bitset_unref); - g_clear_pointer (&self->traceables, gtk_bitset_unref); + g_clear_pointer (&self->ctrdefs, egg_bitset_unref); + g_clear_pointer (&self->ctrsets, egg_bitset_unref); + g_clear_pointer (&self->file_chunks, egg_bitset_unref); + g_clear_pointer (&self->jitmaps, egg_bitset_unref); + g_clear_pointer (&self->mmaps, egg_bitset_unref); + g_clear_pointer (&self->overlays, egg_bitset_unref); + g_clear_pointer (&self->pids, egg_bitset_unref); + g_clear_pointer (&self->processes, egg_bitset_unref); + g_clear_pointer (&self->samples, egg_bitset_unref); + g_clear_pointer (&self->traceables, egg_bitset_unref); g_clear_object (&self->counters); g_clear_pointer (&self->counter_id_to_values, g_hash_table_unref); @@ -250,16 +250,16 @@ sysprof_document_init (SysprofDocument *self) self->counter_id_to_values = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_array_unref); - self->ctrdefs = gtk_bitset_new_empty (); - self->ctrsets = gtk_bitset_new_empty (); - self->file_chunks = gtk_bitset_new_empty (); - self->jitmaps = gtk_bitset_new_empty (); - self->mmaps = gtk_bitset_new_empty (); - self->overlays = gtk_bitset_new_empty (); - self->pids = gtk_bitset_new_empty (); - self->processes = gtk_bitset_new_empty (); - self->samples = gtk_bitset_new_empty (); - self->traceables = gtk_bitset_new_empty (); + self->ctrdefs = egg_bitset_new_empty (); + self->ctrsets = egg_bitset_new_empty (); + self->file_chunks = egg_bitset_new_empty (); + self->jitmaps = egg_bitset_new_empty (); + self->mmaps = egg_bitset_new_empty (); + self->overlays = egg_bitset_new_empty (); + self->pids = egg_bitset_new_empty (); + self->processes = egg_bitset_new_empty (); + self->samples = egg_bitset_new_empty (); + self->traceables = egg_bitset_new_empty (); self->files_first_position = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); self->pid_to_process_info = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)sysprof_process_info_unref); @@ -270,12 +270,12 @@ sysprof_document_init (SysprofDocument *self) static void sysprof_document_load_memory_maps (SysprofDocument *self) { - GtkBitsetIter iter; + EggBitsetIter iter; guint i; g_assert (SYSPROF_IS_DOCUMENT (self)); - if (gtk_bitset_iter_init_first (&iter, self->mmaps, &i)) + if (egg_bitset_iter_init_first (&iter, self->mmaps, &i)) { do { @@ -285,7 +285,7 @@ sysprof_document_load_memory_maps (SysprofDocument *self) sysprof_address_layout_take (process_info->address_layout, g_steal_pointer (&map)); } - while (gtk_bitset_iter_next (&iter, &i)); + while (egg_bitset_iter_next (&iter, &i)); } } @@ -403,12 +403,12 @@ sysprof_document_load_mountinfo (SysprofDocument *self, static void sysprof_document_load_mountinfos (SysprofDocument *self) { - GtkBitsetIter iter; + EggBitsetIter iter; guint pid; g_assert (SYSPROF_IS_DOCUMENT (self)); - if (gtk_bitset_iter_init_first (&iter, self->pids, &pid)) + if (egg_bitset_iter_init_first (&iter, self->pids, &pid)) { do { @@ -422,19 +422,19 @@ sysprof_document_load_mountinfos (SysprofDocument *self) sysprof_document_load_mountinfo (self, pid, bytes); } } - while (gtk_bitset_iter_next (&iter, &pid)); + while (egg_bitset_iter_next (&iter, &pid)); } } static void sysprof_document_load_overlays (SysprofDocument *self) { - GtkBitsetIter iter; + EggBitsetIter iter; guint i; g_assert (SYSPROF_IS_DOCUMENT (self)); - if (gtk_bitset_iter_init_first (&iter, self->overlays, &i)) + if (egg_bitset_iter_init_first (&iter, self->overlays, &i)) { do { @@ -453,19 +453,19 @@ sysprof_document_load_overlays (SysprofDocument *self) g_steal_pointer (&mount)); } } - while (gtk_bitset_iter_next (&iter, &i)); + while (egg_bitset_iter_next (&iter, &i)); } } static void sysprof_document_load_processes (SysprofDocument *self) { - GtkBitsetIter iter; + EggBitsetIter iter; guint i; g_assert (SYSPROF_IS_DOCUMENT (self)); - if (gtk_bitset_iter_init_first (&iter, self->processes, &i)) + if (egg_bitset_iter_init_first (&iter, self->processes, &i)) { do { @@ -489,7 +489,7 @@ sysprof_document_load_processes (SysprofDocument *self) } } } - while (gtk_bitset_iter_next (&iter, &i)); + while (egg_bitset_iter_next (&iter, &i)); } } @@ -497,9 +497,9 @@ static void sysprof_document_load_counters (SysprofDocument *self) { g_autoptr(GPtrArray) counters = NULL; - g_autoptr(GtkBitset) swap_ids = NULL; + g_autoptr(EggBitset) swap_ids = NULL; GListModel *model; - GtkBitsetIter iter; + EggBitsetIter iter; guint i; g_assert (SYSPROF_IS_DOCUMENT (self)); @@ -512,7 +512,7 @@ sysprof_document_load_counters (SysprofDocument *self) * lookup below we can use when reading in counter values. */ if (self->needs_swap) - swap_ids = gtk_bitset_new_empty (); + swap_ids = egg_bitset_new_empty (); /* First create our counter objects which we will use to hold values that * were extracted from the capture file. We create the array first so that @@ -520,7 +520,7 @@ sysprof_document_load_counters (SysprofDocument *self) * each and every added counter. */ counters = g_ptr_array_new_with_free_func (g_object_unref); - if (gtk_bitset_iter_init_first (&iter, self->ctrdefs, &i)) + if (egg_bitset_iter_init_first (&iter, self->ctrdefs, &i)) { do { @@ -540,7 +540,7 @@ sysprof_document_load_counters (SysprofDocument *self) /* Keep track if this counter will need int64 endian swaps */ if (swap_ids != NULL && type == SYSPROF_CAPTURE_COUNTER_INT64) - gtk_bitset_add (swap_ids, id); + egg_bitset_add (swap_ids, id); g_hash_table_insert (self->counter_id_to_values, GUINT_TO_POINTER (id), @@ -555,7 +555,7 @@ sysprof_document_load_counters (SysprofDocument *self) g_steal_pointer (&values))); } } - while (gtk_bitset_iter_next (&iter, &i)); + while (egg_bitset_iter_next (&iter, &i)); if (counters->len > 0) g_list_store_splice (self->counters, 0, 0, counters->pdata, counters->len); @@ -564,7 +564,7 @@ sysprof_document_load_counters (SysprofDocument *self) /* Now find all the counter values and associate them with the counters * that were previously defined. */ - if (gtk_bitset_iter_init_first (&iter, self->ctrsets, &i)) + if (egg_bitset_iter_init_first (&iter, self->ctrsets, &i)) { do { @@ -581,7 +581,7 @@ sysprof_document_load_counters (SysprofDocument *self) sysprof_document_ctrset_get_raw_value (ctrset, j, &id, ctrval.v_raw); - if (swap_ids != NULL && gtk_bitset_contains (swap_ids, id)) + if (swap_ids != NULL && egg_bitset_contains (swap_ids, id)) { #if G_BYTE_ORDER == G_LITTLE_ENDIAN ctrval.v_int64 = GINT64_FROM_BE (ctrval.v_int64); @@ -594,7 +594,7 @@ sysprof_document_load_counters (SysprofDocument *self) g_array_append_val (values, ctrval); } } - while (gtk_bitset_iter_next (&iter, &i)); + while (egg_bitset_iter_next (&iter, &i)); } } @@ -663,45 +663,45 @@ sysprof_document_load_worker (GTask *task, pid = self->needs_swap ? GUINT32_SWAP_LE_BE (tainted->pid) : tainted->pid; - gtk_bitset_add (self->pids, pid); + egg_bitset_add (self->pids, pid); switch ((int)tainted->type) { case SYSPROF_CAPTURE_FRAME_ALLOCATION: - gtk_bitset_add (self->traceables, self->frames->len); + egg_bitset_add (self->traceables, self->frames->len); break; case SYSPROF_CAPTURE_FRAME_SAMPLE: - gtk_bitset_add (self->samples, self->frames->len); - gtk_bitset_add (self->traceables, self->frames->len); + egg_bitset_add (self->samples, self->frames->len); + egg_bitset_add (self->traceables, self->frames->len); break; case SYSPROF_CAPTURE_FRAME_PROCESS: - gtk_bitset_add (self->processes, self->frames->len); + egg_bitset_add (self->processes, self->frames->len); break; case SYSPROF_CAPTURE_FRAME_FILE_CHUNK: - gtk_bitset_add (self->file_chunks, self->frames->len); + egg_bitset_add (self->file_chunks, self->frames->len); break; case SYSPROF_CAPTURE_FRAME_CTRDEF: - gtk_bitset_add (self->ctrdefs, self->frames->len); + egg_bitset_add (self->ctrdefs, self->frames->len); break; case SYSPROF_CAPTURE_FRAME_CTRSET: - gtk_bitset_add (self->ctrsets, self->frames->len); + egg_bitset_add (self->ctrsets, self->frames->len); break; case SYSPROF_CAPTURE_FRAME_JITMAP: - gtk_bitset_add (self->jitmaps, self->frames->len); + egg_bitset_add (self->jitmaps, self->frames->len); break; case SYSPROF_CAPTURE_FRAME_MAP: - gtk_bitset_add (self->mmaps, self->frames->len); + egg_bitset_add (self->mmaps, self->frames->len); break; case SYSPROF_CAPTURE_FRAME_OVERLAY: - gtk_bitset_add (self->overlays, self->frames->len); + egg_bitset_add (self->overlays, self->frames->len); break; default: @@ -903,11 +903,11 @@ sysprof_document_lookup_file (SysprofDocument *self, if (g_hash_table_lookup_extended (self->files_first_position, path, &key, &value)) { g_autoptr(GPtrArray) file_chunks = g_ptr_array_new_with_free_func (g_object_unref); - GtkBitsetIter iter; + EggBitsetIter iter; guint target = GPOINTER_TO_SIZE (value); guint i; - if (gtk_bitset_iter_init_at (&iter, self->file_chunks, target, &i)) + if (egg_bitset_iter_init_at (&iter, self->file_chunks, target, &i)) { do { @@ -923,7 +923,7 @@ sysprof_document_lookup_file (SysprofDocument *self, break; } } - while (gtk_bitset_iter_next (&iter, &i)); + while (egg_bitset_iter_next (&iter, &i)); } return _sysprof_document_file_new (path, g_steal_pointer (&file_chunks)); @@ -944,7 +944,7 @@ GListModel * sysprof_document_list_files (SysprofDocument *self) { GHashTableIter hiter; - GtkBitsetIter iter; + EggBitsetIter iter; GListStore *model; gpointer key, value; @@ -961,7 +961,7 @@ sysprof_document_list_files (SysprofDocument *self) guint target = GPOINTER_TO_SIZE (value); guint i; - if (gtk_bitset_iter_init_at (&iter, self->file_chunks, target, &i)) + if (egg_bitset_iter_init_at (&iter, self->file_chunks, target, &i)) { do { @@ -977,7 +977,7 @@ sysprof_document_list_files (SysprofDocument *self) break; } } - while (gtk_bitset_iter_next (&iter, &i)); + while (egg_bitset_iter_next (&iter, &i)); } file = _sysprof_document_file_new (path, g_steal_pointer (&file_chunks)); From 19b957f4ffc062c6fdb1495a372471bfd071d9de Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 25 May 2023 15:17:46 -0700 Subject: [PATCH 0263/1030] build: remove unnecessary warning ignore This shouldn't be needed anymore. --- meson.build | 7 ------- 1 file changed, 7 deletions(-) diff --git a/meson.build b/meson.build index 320bbe79..2048b186 100644 --- a/meson.build +++ b/meson.build @@ -177,13 +177,6 @@ test_c_args = [ '-Werror=undef', ] -# Until GLib is fixed with regards to volatile type registration -if cc.get_id() == 'clang' - test_c_args += ['-Wno-incompatible-pointer-types'] -else - test_c_args += ['-Werror=incompatible-pointer-types'] -endif - foreach arg: test_c_args if cc.has_multi_arguments(arg) global_c_args += arg From a4b5ea6160135ae4ee2916b6a93b281302d5ddc8 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 25 May 2023 15:30:32 -0700 Subject: [PATCH 0264/1030] build: various meson.build cleanup We have a lot of twisted options, and could really use some cleanup to make that all more manageable. I don't know anywhere we care about not checking for a C++ compiler, so just always check for that so we can use the demangler. --- meson.build | 99 +++++++++++++++++++++++++++++++++-------------------- 1 file changed, 62 insertions(+), 37 deletions(-) diff --git a/meson.build b/meson.build index 2048b186..56045b75 100644 --- a/meson.build +++ b/meson.build @@ -1,4 +1,4 @@ -project('sysprof', 'c', +project('sysprof', ['c', 'cpp'], license: ['GPL3+', 'GPL2+'], version: '45.alpha', meson_version: '>=0.59.0', @@ -33,6 +33,14 @@ datadir = get_option('datadir') datadir_for_pc_file = join_paths('${prefix}', datadir) podir = join_paths(meson.current_source_dir(), 'po') +# Predetermine some features based on meson_options.txt +need_gtk = get_option('gtk') +need_glib = (need_gtk or + get_option('examples') or + get_option('libsysprof') or + get_option('sysprofd') != 'none' or + get_option('agent')) + glib_req = '2.76.0' gtk_req = '4.10' polkit_req = '0.105' @@ -42,11 +50,10 @@ gtk_req_version = '>= @0@'.format(gtk_req) polkit_req_version = '>= @0@'.format(polkit_req) cc = meson.get_compiler('c') +cxx = meson.get_compiler('cpp') -if get_option('libsysprof') or get_option('agent') - add_languages('cpp', native: false) - cxx = meson.get_compiler('cpp') -endif +glib_dep = dependency('glib-2.0', version: glib_req_version, required: need_glib) +gtk_dep = dependency('gtk4', version: gtk_req_version, required: need_gtk) config_h = configuration_data() config_h.set_quoted('SYMBOLIC_VERSION', symbolic_version) @@ -83,19 +90,18 @@ if debugdir == '' endif config_h.set_quoted('DEBUGDIR', debugdir) -config_h.set_quoted('GETTEXT_PACKAGE', 'sysprof') -config_h.set10('ENABLE_NLS', true) -config_h.set_quoted('PACKAGE_LOCALE_DIR', join_paths(get_option('prefix'), get_option('datadir'), 'locale')) -config_h.set('LOCALEDIR', 'PACKAGE_LOCALE_DIR') config_h.set('HAVE_EXECINFO_H', cc.has_header('execinfo.h')) - -config_h.set('HAVE_STRLCPY', cc.has_function('strlcpy')) config_h.set('HAVE_REALLOCARRAY', cc.has_function('reallocarray')) +config_h.set('HAVE_STRLCPY', cc.has_function('strlcpy')) +config_h.set('LOCALEDIR', 'PACKAGE_LOCALE_DIR') +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')) polkit_agent_dep = dependency('polkit-agent-1', required: false) -polkit_dep = dependency('polkit-gobject-1', version: polkit_req_version, required: false) - config_h.set10('HAVE_POLKIT_AGENT', polkit_agent_dep.found()) + +polkit_dep = dependency('polkit-gobject-1', version: polkit_req_version, required: false) config_h.set10('HAVE_POLKIT', polkit_dep.found()) if get_option('libunwind') @@ -103,7 +109,11 @@ if get_option('libunwind') # and backtrace() showing up in builds libunwind_dep = dependency('libunwind-generic', required: true) config_h.set('ENABLE_LIBUNWIND', libunwind_dep.found()) - config_h.set('HAVE_UNW_SET_CACHE_SIZE', libunwind_dep.found() and cc.has_header_symbol('libunwind.h', 'unw_set_cache_size', dependencies: [libunwind_dep])) + config_h.set('HAVE_UNW_SET_CACHE_SIZE', + (libunwind_dep.found() and + cc.has_header_symbol('libunwind.h', + 'unw_set_cache_size', + dependencies: [libunwind_dep]))) endif # Development build setup @@ -111,37 +121,49 @@ if get_option('development') config_h.set10('DEVELOPMENT_BUILD', true) endif -has_use_clockid = cc.has_member('struct perf_event_attr', 'use_clockid', prefix: '#include ') -has_clockid = cc.has_member('struct perf_event_attr', 'clockid', prefix: '#include ') +has_use_clockid = cc.has_member('struct perf_event_attr', + 'use_clockid', + prefix: '#include ') +has_clockid = cc.has_member('struct perf_event_attr', + 'clockid', prefix: + '#include ') config_h.set('HAVE_PERF_CLOCKID', has_use_clockid and has_clockid) -add_project_arguments([ - '-I' + meson.current_build_dir(), # config.h -], language: 'c') - -glib_major = glib_req.split('.')[0].to_int() -glib_minor = glib_req.split('.')[1].to_int() -gtk_major = gtk_req.split('.')[0].to_int() -gtk_minor = gtk_req.split('.')[1].to_int() - -if glib_minor % 2 == 1 - glib_minor = glib_minor + 1 -endif -if gtk_minor % 2 == 1 - gtk_minor = gtk_minor + 1 -endif - +# For config.h +add_project_arguments(['-I'+meson.current_build_dir()], language: 'c') global_c_args = [ '-DSYSPROF_COMPILATION', '-D_GNU_SOURCE', '-D_POSIX_C_SOURCE=200809L', - '-DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_@0@_@1@'.format(glib_major, glib_minor), - '-DGLIB_VERSION_MAX_ALLOWED=GLIB_VERSION_@0@_@1@'.format(glib_major, glib_minor), - '-DGDK_VERSION_MIN_REQUIRED=GDK_VERSION_@0@_@1@'.format(gtk_major, gtk_minor), - '-DGDK_VERSION_MAX_ALLOWED=GDK_VERSION_@0@_@1@'.format(gtk_major, gtk_minor), ] +# Enforce GLib symbol access by required version +if need_glib + glib_major = glib_req.split('.')[0].to_int() + glib_minor = glib_req.split('.')[1].to_int() + if glib_minor % 2 == 1 + glib_minor = glib_minor + 1 + endif + global_c_args += [ + '-DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_@0@_@1@'.format(glib_major, glib_minor), + '-DGLIB_VERSION_MAX_ALLOWED=GLIB_VERSION_@0@_@1@'.format(glib_major, glib_minor), + ] +endif + +# Enforce GTK symbol access by required version +if need_gtk + gtk_major = gtk_req.split('.')[0].to_int() + gtk_minor = gtk_req.split('.')[1].to_int() + if gtk_minor % 2 == 1 + gtk_minor = gtk_minor + 1 + endif + global_c_args += [ + '-DGDK_VERSION_MIN_REQUIRED=GDK_VERSION_@0@_@1@'.format(gtk_major, gtk_minor), + '-DGDK_VERSION_MAX_ALLOWED=GDK_VERSION_@0@_@1@'.format(gtk_major, gtk_minor), + ] +endif + if host_machine.system() == 'darwin' global_c_args += ['-D_DARWIN_C_SOURCE'] endif @@ -231,7 +253,10 @@ endif needs_service_access = get_option('libsysprof') or get_option('agent') install_service_files = needs_service_access or get_option('sysprofd') == 'bundled' -subdir('contrib') +if need_glib + subdir('contrib') +endif + subdir('src') subdir('data') subdir('po') From 24b876f43711b148a0f8b805e491f014b10876d3 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 25 May 2023 15:43:53 -0700 Subject: [PATCH 0265/1030] libsysprof-profile: stub out libsysprof-profile library This does the same thing as we did for libsysprof-analyze, but to contain the profiler bits that will be used from applications/etc. --- meson.build | 5 ++ src/libsysprof-profile/meson.build | 61 ++++++++++++++++++++++++ src/libsysprof-profile/sysprof-profile.h | 28 +++++++++++ src/libsysprof-profile/tests/meson.build | 0 src/meson.build | 13 ++++- 5 files changed, 105 insertions(+), 2 deletions(-) create mode 100644 src/libsysprof-profile/meson.build create mode 100644 src/libsysprof-profile/sysprof-profile.h create mode 100644 src/libsysprof-profile/tests/meson.build diff --git a/meson.build b/meson.build index 56045b75..dfe65e1b 100644 --- a/meson.build +++ b/meson.build @@ -41,6 +41,11 @@ need_glib = (need_gtk or get_option('sysprofd') != 'none' or get_option('agent')) +# Determine what libraries to build +need_libsysprof_capture = true +need_libsysprof_profile = get_option('libsysprof') or get_option('agent') +need_libsysprof_analyze = get_option('libsysprof') or get_option('agent') + glib_req = '2.76.0' gtk_req = '4.10' polkit_req = '0.105' diff --git a/src/libsysprof-profile/meson.build b/src/libsysprof-profile/meson.build new file mode 100644 index 00000000..8d414a74 --- /dev/null +++ b/src/libsysprof-profile/meson.build @@ -0,0 +1,61 @@ +libsysprof_profile_public_sources = [ +] + +libsysprof_profile_private_sources = [ +] + +libsysprof_profile_public_headers = [ + 'sysprof-profile.h', +] + +libsysprof_profile_deps = [ + dependency('gio-2.0', version: glib_req_version), + + libsysprof_capture_dep, +] + +libsysprof_profile_static = static_library( + 'sysprof-profile-@0@'.format(soname_major_version), + libsysprof_profile_public_sources + + libsysprof_profile_private_sources, + + include_directories: [include_directories('.'), + ipc_include_dirs, + libsysprof_capture_include_dirs], + dependencies: libsysprof_profile_deps, + gnu_symbol_visibility: 'hidden', +) + +libsysprof_profile_static_dep = declare_dependency( + link_with: libsysprof_profile_static, + dependencies: libsysprof_profile_deps, + include_directories: [include_directories('.'), + libsysprof_capture_include_dirs], +) + +libsysprof_profile = library('sysprof-profile-@0@'.format(soname_major_version), + dependencies: [libsysprof_profile_static_dep], + gnu_symbol_visibility: 'hidden', + version: '@0@.0.0'.format(soname_major_version), + darwin_versions: '@0@.0'.format(soname_major_version), + install: true, +) + +libsysprof_profile_dep = declare_dependency( + link_with: libsysprof_profile, + dependencies: libsysprof_profile_deps, + include_directories: [include_directories('.'), libsysprof_capture_include_dirs], +) +meson.override_dependency('sysprof-profile-@0@'.format(soname_major_version), libsysprof_profile_dep) + +pkgconfig.generate(libsysprof_profile, + subdirs: [sysprof_header_subdir], + description: 'A library for recording profiles using various instruments', + install_dir: join_paths(get_option('libdir'), 'pkgconfig'), + requires: ['gio-2.0'], + variables: ['datadir=' + datadir_for_pc_file], +) + +install_headers(libsysprof_profile_public_headers, subdir: sysprof_header_subdir) + +subdir('tests') diff --git a/src/libsysprof-profile/sysprof-profile.h b/src/libsysprof-profile/sysprof-profile.h new file mode 100644 index 00000000..8b2c7af7 --- /dev/null +++ b/src/libsysprof-profile/sysprof-profile.h @@ -0,0 +1,28 @@ +/* sysprof-profile.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +G_BEGIN_DECLS + +#define SYSPROF_PROFILE_INSIDE +#undef SYSPROF_PROFILE_INSIDE + +G_END_DECLS diff --git a/src/libsysprof-profile/tests/meson.build b/src/libsysprof-profile/tests/meson.build new file mode 100644 index 00000000..e69de29b diff --git a/src/meson.build b/src/meson.build index a74f2754..c735b46c 100644 --- a/src/meson.build +++ b/src/meson.build @@ -54,8 +54,17 @@ stackstash_sources = files('stackstash.c') helpers_sources = files('helpers.c') -subdir('libsysprof-capture') -subdir('libsysprof-analyze') +if need_libsysprof_capture + subdir('libsysprof-capture') +endif + +if need_libsysprof_analyze + subdir('libsysprof-analyze') +endif + +if need_libsysprof_profile + subdir('libsysprof-profile') +endif if get_option('sysprofd') == 'bundled' subdir('sysprofd') From 93153d19436f682b4225c7acc5d30a2292a98bf9 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 25 May 2023 16:21:40 -0700 Subject: [PATCH 0266/1030] libsysprof-profile: add scaffolding for base objects The goal here is to have a fairly small exposed API surface for profiling similar to libsysprof-analyze where implementation details are hidden. SysprofProfiler - Where you setup your recording SysprofInstrument - What you add to a profiler to extract data SysprofRecording - Represents an active recording w/ instruments --- src/libsysprof-profile/meson.build | 7 + .../sysprof-instrument-private.h | 39 ++++ src/libsysprof-profile/sysprof-instrument.c | 35 +++ src/libsysprof-profile/sysprof-instrument.h | 42 ++++ src/libsysprof-profile/sysprof-profiler.c | 140 ++++++++++++ src/libsysprof-profile/sysprof-profiler.h | 52 +++++ .../sysprof-recording-private.h | 29 +++ src/libsysprof-profile/sysprof-recording.c | 205 ++++++++++++++++++ src/libsysprof-profile/sysprof-recording.h | 46 ++++ 9 files changed, 595 insertions(+) create mode 100644 src/libsysprof-profile/sysprof-instrument-private.h create mode 100644 src/libsysprof-profile/sysprof-instrument.c create mode 100644 src/libsysprof-profile/sysprof-instrument.h create mode 100644 src/libsysprof-profile/sysprof-profiler.c create mode 100644 src/libsysprof-profile/sysprof-profiler.h create mode 100644 src/libsysprof-profile/sysprof-recording-private.h create mode 100644 src/libsysprof-profile/sysprof-recording.c create mode 100644 src/libsysprof-profile/sysprof-recording.h diff --git a/src/libsysprof-profile/meson.build b/src/libsysprof-profile/meson.build index 8d414a74..5ce52170 100644 --- a/src/libsysprof-profile/meson.build +++ b/src/libsysprof-profile/meson.build @@ -1,4 +1,7 @@ libsysprof_profile_public_sources = [ + 'sysprof-instrument.c', + 'sysprof-profiler.c', + 'sysprof-recording.c', ] libsysprof_profile_private_sources = [ @@ -6,6 +9,10 @@ libsysprof_profile_private_sources = [ libsysprof_profile_public_headers = [ 'sysprof-profile.h', + + 'sysprof-instrument.h', + 'sysprof-profiler.h', + 'sysprof-recording.h', ] libsysprof_profile_deps = [ diff --git a/src/libsysprof-profile/sysprof-instrument-private.h b/src/libsysprof-profile/sysprof-instrument-private.h new file mode 100644 index 00000000..3f3e37c3 --- /dev/null +++ b/src/libsysprof-profile/sysprof-instrument-private.h @@ -0,0 +1,39 @@ +/* sysprof-instrument-private.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-instrument.h" + +G_BEGIN_DECLS + +#define SYSPROF_INSTRUMENT_GET_CLASS(obj) G_TYPE_INSTANCE_GET_CLASS(obj, SYSPROF_TYPE_INSTRUMENT, SysprofInstrumentClass) + +struct _SysprofInstrument +{ + GObject parent; +}; + +struct _SysprofInstrumentClass +{ + GObjectClass parent_class; +}; + +G_END_DECLS diff --git a/src/libsysprof-profile/sysprof-instrument.c b/src/libsysprof-profile/sysprof-instrument.c new file mode 100644 index 00000000..8f9b55a2 --- /dev/null +++ b/src/libsysprof-profile/sysprof-instrument.c @@ -0,0 +1,35 @@ +/* sysprof-instrument.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-instrument-private.h" + +G_DEFINE_ABSTRACT_TYPE (SysprofInstrument, sysprof_instrument, G_TYPE_OBJECT) + +static void +sysprof_instrument_class_init (SysprofInstrumentClass *klass) +{ +} + +static void +sysprof_instrument_init (SysprofInstrument *self) +{ +} diff --git a/src/libsysprof-profile/sysprof-instrument.h b/src/libsysprof-profile/sysprof-instrument.h new file mode 100644 index 00000000..7b09b779 --- /dev/null +++ b/src/libsysprof-profile/sysprof-instrument.h @@ -0,0 +1,42 @@ +/* sysprof-instrument.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +#include + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_INSTRUMENT (sysprof_instrumentr_get_type()) +#define SYSPROF_IS_INSTRUMENT(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_INSTRUMENT) +#define SYSPROF_INSTRUMENT(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_INSTRUMENT, SysprofInstrument) +#define SYSPROF_INSTRUMENT_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_INSTRUMENT, SysprofInstrumentClass) + +typedef struct _SysprofInstrument SysprofInstrument; +typedef struct _SysprofInstrumentClass SysprofInstrumentClass; + +SYSPROF_AVAILABLE_IN_ALL +GType sysprof_instrumentr_get_type (void) G_GNUC_CONST; + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofInstrument, g_object_unref) + +G_END_DECLS diff --git a/src/libsysprof-profile/sysprof-profiler.c b/src/libsysprof-profile/sysprof-profiler.c new file mode 100644 index 00000000..5598f13b --- /dev/null +++ b/src/libsysprof-profile/sysprof-profiler.c @@ -0,0 +1,140 @@ +/* sysprof-profiler.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-profiler.h" + +struct _SysprofProfiler +{ + GObject parent_instance; + GPtrArray *instruments; +}; + +enum { + PROP_0, + N_PROPS +}; + +G_DEFINE_FINAL_TYPE (SysprofProfiler, sysprof_profiler, G_TYPE_OBJECT) + +static void +sysprof_profiler_finalize (GObject *object) +{ + G_OBJECT_CLASS (sysprof_profiler_parent_class)->finalize (object); +} + +static void +sysprof_profiler_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + switch (prop_id) + { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_profiler_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (prop_id) + { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_profiler_class_init (SysprofProfilerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = sysprof_profiler_finalize; + object_class->get_property = sysprof_profiler_get_property; + object_class->set_property = sysprof_profiler_set_property; +} + +static void +sysprof_profiler_init (SysprofProfiler *self) +{ + self->instruments = g_ptr_array_new_with_free_func (g_object_unref); +} + +SysprofProfiler * +sysprof_profiler_new (void) +{ + return g_object_new (SYSPROF_TYPE_PROFILER, NULL); +} + +void +sysprof_profiler_add_instrument (SysprofProfiler *self, + SysprofInstrument *instrument) +{ + g_return_if_fail (SYSPROF_IS_PROFILER (self)); + g_return_if_fail (SYSPROF_IS_INSTRUMENT (instrument)); + + g_ptr_array_add (self->instruments, g_object_ref (instrument)); +} + +void +sysprof_profiler_record_async (SysprofProfiler *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_autoptr(GTask) task = NULL; + + g_return_if_fail (SYSPROF_IS_PROFILER (self)); + g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); + + task = g_task_new (self, cancellable, callback, user_data); + g_task_set_source_tag (task, sysprof_profiler_record_async); + + +} + +/** + * sysprof_profiler_record_finish: + * @self: a #SysprofProfiler + * @result: a #GAsyncResult + * @error: a location for a #GError + * + * Completes an asynchronous request to start recording. + * + * Returns: (transfer full): an active #SysprofRecording if successful; + * otherwise %NULL and @error is set. + */ +SysprofRecording * +sysprof_profiler_record_finish (SysprofProfiler *self, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (SYSPROF_IS_PROFILER (self), NULL); + g_return_val_if_fail (G_IS_TASK (result), NULL); + g_return_val_if_fail (g_task_is_valid (result, self), NULL); + + return g_task_propagate_pointer (G_TASK (result), error); +} diff --git a/src/libsysprof-profile/sysprof-profiler.h b/src/libsysprof-profile/sysprof-profiler.h new file mode 100644 index 00000000..c1ec9857 --- /dev/null +++ b/src/libsysprof-profile/sysprof-profiler.h @@ -0,0 +1,52 @@ +/* sysprof-profiler.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +#include + +#include "sysprof-instrument.h" +#include "sysprof-recording.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_PROFILER (sysprof_profiler_get_type()) + +SYSPROF_AVAILABLE_IN_ALL +G_DECLARE_FINAL_TYPE (SysprofProfiler, sysprof_profiler, SYSPROF, PROFILER, GObject) + +SYSPROF_AVAILABLE_IN_ALL +SysprofProfiler *sysprof_profiler_new (void); +SYSPROF_AVAILABLE_IN_ALL +void sysprof_profiler_add_instrument (SysprofProfiler *self, + SysprofInstrument *instrument); +SYSPROF_AVAILABLE_IN_ALL +void sysprof_profiler_record_async (SysprofProfiler *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +SYSPROF_AVAILABLE_IN_ALL +SysprofRecording *sysprof_profiler_record_finish (SysprofProfiler *self, + GAsyncResult *result, + GError **error); + +G_END_DECLS diff --git a/src/libsysprof-profile/sysprof-recording-private.h b/src/libsysprof-profile/sysprof-recording-private.h new file mode 100644 index 00000000..f09ed11a --- /dev/null +++ b/src/libsysprof-profile/sysprof-recording-private.h @@ -0,0 +1,29 @@ +/* sysprof-recording-private.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-recording.h" + +G_BEGIN_DECLS + +SysprofRecording *_sysprof_recording_new (void); + +G_END_DECLS diff --git a/src/libsysprof-profile/sysprof-recording.c b/src/libsysprof-profile/sysprof-recording.c new file mode 100644 index 00000000..676edaa2 --- /dev/null +++ b/src/libsysprof-profile/sysprof-recording.c @@ -0,0 +1,205 @@ +/* sysprof-recording.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-recording-private.h" + +typedef enum _SysprofRecordingState +{ + SYSPROF_RECORDING_STATE_INITIAL, + SYSPROF_RECORDING_STATE_PRE, + SYSPROF_RECORDING_STATE_RECORD, + SYSPROF_RECORDING_STATE_POST, + SYSPROF_RECORDING_STATE_FINISHED, + SYSPROF_RECORDING_STATE_ERROR, +} SysprofRecordingState; + +struct _SysprofRecording +{ + GObject parent_instance; + + SysprofRecordingState state : 3; + + GQueue waiters; +}; + +enum { + PROP_0, + N_PROPS +}; + +G_DEFINE_FINAL_TYPE (SysprofRecording, sysprof_recording, G_TYPE_OBJECT) + +static gboolean +sysprof_recording_state_is_terminal (SysprofRecordingState state) +{ + return state == SYSPROF_RECORDING_STATE_ERROR || + state == SYSPROF_RECORDING_STATE_FINISHED; +} + +static void +sysprof_recording_set_state (SysprofRecording *self, + SysprofRecordingState state) +{ + GQueue waiters; + GTask *task; + + g_assert (SYSPROF_IS_RECORDING (self)); + + self->state = state; + + if (!sysprof_recording_state_is_terminal (state)) + return; + + waiters = self->waiters; + self->waiters = (GQueue) {NULL, NULL, 0}; + + while ((task = g_queue_pop_head (&waiters))) + { + if (state == SYSPROF_RECORDING_STATE_ERROR) + g_task_return_new_error (task, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "Recording failed"); + else + g_task_return_boolean (task, TRUE); + + g_object_unref (task); + } +} + +static void +sysprof_recording_finalize (GObject *object) +{ + G_OBJECT_CLASS (sysprof_recording_parent_class)->finalize (object); +} + +static void +sysprof_recording_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + switch (prop_id) + { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_recording_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (prop_id) + { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_recording_class_init (SysprofRecordingClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = sysprof_recording_finalize; + object_class->get_property = sysprof_recording_get_property; + object_class->set_property = sysprof_recording_set_property; +} + +static void +sysprof_recording_init (SysprofRecording *self) +{ + sysprof_recording_set_state (self, SYSPROF_RECORDING_STATE_INITIAL); +} + +SysprofRecording * +_sysprof_recording_new (void) +{ + return g_object_new (SYSPROF_TYPE_RECORDING, NULL); +} + +void +sysprof_recording_stop (SysprofRecording *self) +{ + g_return_if_fail (SYSPROF_IS_RECORDING (self)); + +} + +void +sysprof_recording_wait_async (SysprofRecording *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_autoptr(GTask) task = NULL; + + g_return_if_fail (SYSPROF_IS_RECORDING (self)); + g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); + + task = g_task_new (self, cancellable, callback, user_data); + g_task_set_source_tag (task, sysprof_recording_wait_async); + + switch (self->state) + { + case SYSPROF_RECORDING_STATE_INITIAL: + g_task_return_new_error (task, + G_IO_ERROR, + G_IO_ERROR_INVAL, + "Recording has not yet started"); + break; + + case SYSPROF_RECORDING_STATE_ERROR: + g_task_return_new_error (task, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "Recording failed"); + break; + + case SYSPROF_RECORDING_STATE_FINISHED: + g_task_return_boolean (task, TRUE); + break; + + case SYSPROF_RECORDING_STATE_PRE: + case SYSPROF_RECORDING_STATE_RECORD: + case SYSPROF_RECORDING_STATE_POST: + g_queue_push_tail (&self->waiters, g_steal_pointer (&task)); + break; + + default: + g_assert_not_reached (); + } +} + +gboolean +sysprof_recording_wait_finish (SysprofRecording *self, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (SYSPROF_IS_RECORDING (self), FALSE); + g_return_val_if_fail (G_IS_TASK (result), FALSE); + g_return_val_if_fail (g_task_is_valid (result, self), FALSE); + + return g_task_propagate_boolean (G_TASK (result), error); +} diff --git a/src/libsysprof-profile/sysprof-recording.h b/src/libsysprof-profile/sysprof-recording.h new file mode 100644 index 00000000..1758ab80 --- /dev/null +++ b/src/libsysprof-profile/sysprof-recording.h @@ -0,0 +1,46 @@ +/* sysprof-recording.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +#include + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_RECORDING (sysprof_recording_get_type()) + +SYSPROF_AVAILABLE_IN_ALL +G_DECLARE_FINAL_TYPE (SysprofRecording, sysprof_recording, SYSPROF, RECORDING, GObject) + +SYSPROF_AVAILABLE_IN_ALL +void sysprof_recording_wait_async (SysprofRecording *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +SYSPROF_AVAILABLE_IN_ALL +gboolean sysprof_recording_wait_finish (SysprofRecording *self, + GAsyncResult *result, + GError **error); +SYSPROF_AVAILABLE_IN_ALL +void sysprof_recording_stop (SysprofRecording *self); + +G_END_DECLS From 2ae33917b279ae645861ae0d16cb0c5dc5fb3f38 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 25 May 2023 16:26:18 -0700 Subject: [PATCH 0267/1030] libsysprof-profile: add writer to recording object --- .../sysprof-recording-private.h | 3 ++- src/libsysprof-profile/sysprof-recording.c | 24 ++++++++++++++++--- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/libsysprof-profile/sysprof-recording-private.h b/src/libsysprof-profile/sysprof-recording-private.h index f09ed11a..ed5580dc 100644 --- a/src/libsysprof-profile/sysprof-recording-private.h +++ b/src/libsysprof-profile/sysprof-recording-private.h @@ -24,6 +24,7 @@ G_BEGIN_DECLS -SysprofRecording *_sysprof_recording_new (void); +SysprofRecording *_sysprof_recording_new (SysprofCaptureWriter *writer); +SysprofCaptureWriter *_sysprof_recording_writer (SysprofRecording *self); G_END_DECLS diff --git a/src/libsysprof-profile/sysprof-recording.c b/src/libsysprof-profile/sysprof-recording.c index 676edaa2..49cd0bf2 100644 --- a/src/libsysprof-profile/sysprof-recording.c +++ b/src/libsysprof-profile/sysprof-recording.c @@ -36,9 +36,20 @@ struct _SysprofRecording { GObject parent_instance; - SysprofRecordingState state : 3; + /* This is where all of the instruments will write to. They are + * expected to do this from the main-thread only. To work from + * additional threads they need to proxy that state to the + * main thread for writing. + */ + SysprofCaptureWriter *writer; + /* Waiters contains a list of GTask to complete calls to the + * sysprof_recording_wait_async() flow. + */ GQueue waiters; + + /* Our current state of operation */ + SysprofRecordingState state : 3; }; enum { @@ -135,9 +146,16 @@ sysprof_recording_init (SysprofRecording *self) } SysprofRecording * -_sysprof_recording_new (void) +_sysprof_recording_new (SysprofCaptureWriter *writer) { - return g_object_new (SYSPROF_TYPE_RECORDING, NULL); + SysprofRecording *self; + + g_return_val_if_fail (writer != NULL, NULL); + + self = g_object_new (SYSPROF_TYPE_RECORDING, NULL); + self->writer = sysprof_capture_writer_ref (writer); + + return self; } void From ccdc1b8cffeb42b630769e902c5fd5c00faebe84 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 25 May 2023 16:43:56 -0700 Subject: [PATCH 0268/1030] libsysprof-profile: add API to list required policy The idea here is that we stop having instruments do their own policy checking and instead do the policy checking as a set from the recording as part of prepare/etc. --- .../sysprof-instrument-private.h | 4 ++++ src/libsysprof-profile/sysprof-instrument.c | 15 +++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/src/libsysprof-profile/sysprof-instrument-private.h b/src/libsysprof-profile/sysprof-instrument-private.h index 3f3e37c3..bc3b45e2 100644 --- a/src/libsysprof-profile/sysprof-instrument-private.h +++ b/src/libsysprof-profile/sysprof-instrument-private.h @@ -34,6 +34,10 @@ struct _SysprofInstrument struct _SysprofInstrumentClass { GObjectClass parent_class; + + char **(*list_required_policy) (SysprofInstrument *self); }; +char **_sysprof_instrument_list_required_policy (SysprofInstrument *self); + G_END_DECLS diff --git a/src/libsysprof-profile/sysprof-instrument.c b/src/libsysprof-profile/sysprof-instrument.c index 8f9b55a2..baaae8b7 100644 --- a/src/libsysprof-profile/sysprof-instrument.c +++ b/src/libsysprof-profile/sysprof-instrument.c @@ -24,12 +24,27 @@ G_DEFINE_ABSTRACT_TYPE (SysprofInstrument, sysprof_instrument, G_TYPE_OBJECT) +static char ** +sysprof_instrument_real_list_required_policy (SysprofInstrument *self) +{ + return NULL; +} + static void sysprof_instrument_class_init (SysprofInstrumentClass *klass) { + klass->list_required_policy = sysprof_instrument_real_list_required_policy; } static void sysprof_instrument_init (SysprofInstrument *self) { } + +char ** +_sysprof_instrument_list_required_policy (SysprofInstrument *self) +{ + g_return_val_if_fail (SYSPROF_IS_INSTRUMENT (self), NULL); + + return SYSPROF_INSTRUMENT_GET_CLASS (self)->list_required_policy (self); +} From 08aa970c7c81c94fb7ea56398ab6add5ed49d3f0 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 25 May 2023 17:05:16 -0700 Subject: [PATCH 0269/1030] libsysprof-analyze: add instruments to recording and list policy We need to know all the policy that must be acquired upfront so that we don't have to do individual queries on policy. --- .../sysprof-recording-private.h | 8 ++- src/libsysprof-profile/sysprof-recording.c | 65 ++++++++++++++++++- 2 files changed, 70 insertions(+), 3 deletions(-) diff --git a/src/libsysprof-profile/sysprof-recording-private.h b/src/libsysprof-profile/sysprof-recording-private.h index ed5580dc..67852820 100644 --- a/src/libsysprof-profile/sysprof-recording-private.h +++ b/src/libsysprof-profile/sysprof-recording-private.h @@ -20,11 +20,15 @@ #pragma once +#include "sysprof-instrument.h" #include "sysprof-recording.h" G_BEGIN_DECLS -SysprofRecording *_sysprof_recording_new (SysprofCaptureWriter *writer); -SysprofCaptureWriter *_sysprof_recording_writer (SysprofRecording *self); +SysprofRecording *_sysprof_recording_new (SysprofCaptureWriter *writer, + SysprofInstrument **instruments, + guint n_instruments); +void _sysprof_recording_start (SysprofRecording *self); +SysprofCaptureWriter *_sysprof_recording_writer (SysprofRecording *self); G_END_DECLS diff --git a/src/libsysprof-profile/sysprof-recording.c b/src/libsysprof-profile/sysprof-recording.c index 49cd0bf2..85be25ca 100644 --- a/src/libsysprof-profile/sysprof-recording.c +++ b/src/libsysprof-profile/sysprof-recording.c @@ -20,6 +20,7 @@ #include "config.h" +#include "sysprof-instrument-private.h" #include "sysprof-recording-private.h" typedef enum _SysprofRecordingState @@ -43,6 +44,9 @@ struct _SysprofRecording */ SysprofCaptureWriter *writer; + /* An array of SysprofInstrument that are part of this recording */ + GPtrArray *instruments; + /* Waiters contains a list of GTask to complete calls to the * sysprof_recording_wait_async() flow. */ @@ -142,11 +146,15 @@ sysprof_recording_class_init (SysprofRecordingClass *klass) static void sysprof_recording_init (SysprofRecording *self) { + self->instruments = g_ptr_array_new_with_free_func (g_object_unref); + sysprof_recording_set_state (self, SYSPROF_RECORDING_STATE_INITIAL); } SysprofRecording * -_sysprof_recording_new (SysprofCaptureWriter *writer) +_sysprof_recording_new (SysprofCaptureWriter *writer, + SysprofInstrument **instruments, + guint n_instruments) { SysprofRecording *self; @@ -155,9 +163,64 @@ _sysprof_recording_new (SysprofCaptureWriter *writer) self = g_object_new (SYSPROF_TYPE_RECORDING, NULL); self->writer = sysprof_capture_writer_ref (writer); + for (guint i = 0; i < n_instruments; i++) + g_ptr_array_add (self->instruments, g_object_ref (instruments[i])); + return self; } +static char ** +sysprof_recording_list_required_policy (SysprofRecording *self) +{ + g_autoptr(GPtrArray) all_policy = NULL; + + g_assert (SYSPROF_IS_RECORDING (self)); + + all_policy = g_ptr_array_new_null_terminated (0, g_free, TRUE); + + for (guint i = 0; i > self->instruments->len; i++) + { + SysprofInstrument *instrument = g_ptr_array_index (self->instruments, i); + g_auto(GStrv) policy = _sysprof_instrument_list_required_policy (instrument); + + if (policy == NULL) + continue; + + for (guint j = 0; policy[j]; j++) + { + gboolean found = FALSE; + + for (guint k = 0; !found && k < all_policy->len; k++) + found = strcmp (policy[j], g_ptr_array_index (all_policy, k)) == 0; + + if (!found) + g_ptr_array_add (all_policy, g_strdup (policy[j])); + } + } + + if (all_policy->len == 0) + return NULL; + + return (char **)g_ptr_array_free (all_policy, TRUE); +} + +void +_sysprof_recording_start (SysprofRecording *self) +{ + + g_auto(GStrv) required_policy = NULL; + + g_return_if_fail (SYSPROF_IS_RECORDING (self)); + g_return_if_fail (self->state == SYSPROF_RECORDING_STATE_INITIAL); + + sysprof_recording_set_state (self, SYSPROF_RECORDING_STATE_PRE); + + if ((required_policy = sysprof_recording_list_required_policy (self))) + { + /* TODO: Query policy kit for policy */ + } +} + void sysprof_recording_stop (SysprofRecording *self) { From 7b83ff3f3491a4175b2e06b738ff13c5d4a39b65 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 25 May 2023 17:10:23 -0700 Subject: [PATCH 0270/1030] libsysprof-profile: fix includes for profiler --- src/libsysprof-profile/sysprof-profile.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/libsysprof-profile/sysprof-profile.h b/src/libsysprof-profile/sysprof-profile.h index 8b2c7af7..9dae599c 100644 --- a/src/libsysprof-profile/sysprof-profile.h +++ b/src/libsysprof-profile/sysprof-profile.h @@ -20,9 +20,14 @@ #pragma once +#include + G_BEGIN_DECLS #define SYSPROF_PROFILE_INSIDE +# include "sysprof-instrument.h" +# include "sysprof-profiler.h" +# include "sysprof-recording.h" #undef SYSPROF_PROFILE_INSIDE G_END_DECLS From d3fe38295471aa52a9647d2fd78ff142071b669d Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 25 May 2023 17:10:37 -0700 Subject: [PATCH 0271/1030] libsysprof-profile: add test-profiler --- src/libsysprof-profile/tests/meson.build | 30 +++++++++++++ src/libsysprof-profile/tests/test-profiler.c | 45 ++++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 src/libsysprof-profile/tests/test-profiler.c diff --git a/src/libsysprof-profile/tests/meson.build b/src/libsysprof-profile/tests/meson.build index e69de29b..f559ff7e 100644 --- a/src/libsysprof-profile/tests/meson.build +++ b/src/libsysprof-profile/tests/meson.build @@ -0,0 +1,30 @@ +libsysprof_profile_test_env = [ + 'G_DEBUG=gc-friendly', + 'GSETTINGS_BACKEND=memory', + 'MALLOC_CHECK_=2', +] + +libsysprof_profile_testsuite_c_args = [ + '-DG_LOG_DOMAIN="libdex"', + '-DG_ENABLE_DEBUG', + '-UG_DISABLE_ASSERT', + '-UG_DISABLE_CAST_CHECKS', +] + +libsysprof_profile_testsuite = { + 'test-profiler' : {'skip': true}, +} + +libsysprof_profile_testsuite_deps = [ + libsysprof_profile_static_dep, +] + +foreach test, params: libsysprof_profile_testsuite + test_exe = executable(test, '@0@.c'.format(test), + c_args: libsysprof_profile_testsuite_c_args, + dependencies: libsysprof_profile_testsuite_deps, + ) + if not params.get('skip', false) + test(test, test_exe, env: libsysprof_profile_test_env) + endif +endforeach diff --git a/src/libsysprof-profile/tests/test-profiler.c b/src/libsysprof-profile/tests/test-profiler.c new file mode 100644 index 00000000..ef1850af --- /dev/null +++ b/src/libsysprof-profile/tests/test-profiler.c @@ -0,0 +1,45 @@ +/* test-profiler.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include + +static char *capture_file; +static const GOptionEntry entries[] = { + { "capture", 'c', 0, G_OPTION_ARG_FILENAME, &capture_file, "The file to capture into", "CAPTURE" }, + { 0 } +}; + +int +main (int argc, + char *argv[]) +{ + g_autoptr(GOptionContext) context = g_option_context_new ("- Tests the SysprofProfiler"); + g_autoptr(GError) error = NULL; + + g_option_context_add_main_entries (context, entries, NULL); + + if (!g_option_context_parse (context, &argc, &argv, &error)) + { + g_printerr ("%s\n", error->message); + return 1; + } + + return 0; +} From 9498d15170c4bdd78347d29f16c9f8e9486ca263 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 25 May 2023 17:16:47 -0700 Subject: [PATCH 0272/1030] libsysprof-profile: fix copy-paste typo --- src/libsysprof-profile/sysprof-instrument.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libsysprof-profile/sysprof-instrument.h b/src/libsysprof-profile/sysprof-instrument.h index 7b09b779..ed2f6980 100644 --- a/src/libsysprof-profile/sysprof-instrument.h +++ b/src/libsysprof-profile/sysprof-instrument.h @@ -26,7 +26,7 @@ G_BEGIN_DECLS -#define SYSPROF_TYPE_INSTRUMENT (sysprof_instrumentr_get_type()) +#define SYSPROF_TYPE_INSTRUMENT (sysprof_instrument_get_type()) #define SYSPROF_IS_INSTRUMENT(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_INSTRUMENT) #define SYSPROF_INSTRUMENT(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_INSTRUMENT, SysprofInstrument) #define SYSPROF_INSTRUMENT_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_INSTRUMENT, SysprofInstrumentClass) @@ -35,7 +35,7 @@ typedef struct _SysprofInstrument SysprofInstrument; typedef struct _SysprofInstrumentClass SysprofInstrumentClass; SYSPROF_AVAILABLE_IN_ALL -GType sysprof_instrumentr_get_type (void) G_GNUC_CONST; +GType sysprof_instrument_get_type (void) G_GNUC_CONST; G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofInstrument, g_object_unref) From a8fbb645d09d04e15590ed0ea8d948f7563c923a Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 25 May 2023 17:17:04 -0700 Subject: [PATCH 0273/1030] libsysprof-profile: add writer as record parameter --- src/libsysprof-profile/sysprof-profiler.c | 10 ++++++---- src/libsysprof-profile/sysprof-profiler.h | 1 + 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/libsysprof-profile/sysprof-profiler.c b/src/libsysprof-profile/sysprof-profiler.c index 5598f13b..155cdceb 100644 --- a/src/libsysprof-profile/sysprof-profiler.c +++ b/src/libsysprof-profile/sysprof-profiler.c @@ -100,14 +100,16 @@ sysprof_profiler_add_instrument (SysprofProfiler *self, } void -sysprof_profiler_record_async (SysprofProfiler *self, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) +sysprof_profiler_record_async (SysprofProfiler *self, + SysprofCaptureWriter *writer, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { g_autoptr(GTask) task = NULL; g_return_if_fail (SYSPROF_IS_PROFILER (self)); + g_return_if_fail (writer != NULL); g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); task = g_task_new (self, cancellable, callback, user_data); diff --git a/src/libsysprof-profile/sysprof-profiler.h b/src/libsysprof-profile/sysprof-profiler.h index c1ec9857..a8be07f5 100644 --- a/src/libsysprof-profile/sysprof-profiler.h +++ b/src/libsysprof-profile/sysprof-profiler.h @@ -41,6 +41,7 @@ void sysprof_profiler_add_instrument (SysprofProfiler *self, SysprofInstrument *instrument); SYSPROF_AVAILABLE_IN_ALL void sysprof_profiler_record_async (SysprofProfiler *self, + SysprofCaptureWriter *writer, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); From 7fb77ee01d78f2fdef607285cfabf8d4cce65d80 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 25 May 2023 17:17:25 -0700 Subject: [PATCH 0274/1030] libsysprof-profile: create writer and test record --- src/libsysprof-profile/tests/test-profiler.c | 33 +++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/src/libsysprof-profile/tests/test-profiler.c b/src/libsysprof-profile/tests/test-profiler.c index ef1850af..2a6fb469 100644 --- a/src/libsysprof-profile/tests/test-profiler.c +++ b/src/libsysprof-profile/tests/test-profiler.c @@ -20,18 +20,36 @@ #include +static GMainLoop *main_loop; static char *capture_file; static const GOptionEntry entries[] = { { "capture", 'c', 0, G_OPTION_ARG_FILENAME, &capture_file, "The file to capture into", "CAPTURE" }, { 0 } }; +static void +record_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + g_autoptr(GError) error = NULL; + g_autoptr(SysprofRecording) recording = sysprof_profiler_record_finish (SYSPROF_PROFILER (object), result, &error); + + g_assert_no_error (error); + g_assert_nonnull (recording); + g_assert_true (SYSPROF_IS_RECORDING (recording)); +} + int -main (int argc, +main (int argc, char *argv[]) { g_autoptr(GOptionContext) context = g_option_context_new ("- Tests the SysprofProfiler"); + g_autoptr(SysprofProfiler) profiler = NULL; g_autoptr(GError) error = NULL; + SysprofCaptureWriter *writer = NULL; + + main_loop = g_main_loop_new (NULL, FALSE); g_option_context_add_main_entries (context, entries, NULL); @@ -41,5 +59,18 @@ main (int argc, return 1; } + if (capture_file == NULL) + writer = sysprof_capture_writer_new ("capture.syscap", 0); + else + writer = sysprof_capture_writer_new (capture_file, 0); + + profiler = sysprof_profiler_new (); + + sysprof_profiler_record_async (profiler, writer, NULL, record_cb, NULL); + + g_main_loop_run (main_loop); + + g_clear_pointer (&writer, sysprof_capture_writer_unref); + return 0; } From 07dc728bfe524af14d0de0ecae3afd7f01a5ef05 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 25 May 2023 17:24:33 -0700 Subject: [PATCH 0275/1030] libsysprof-profile: change state on stop We will need some more checks in here later on once we're doing things with the instruments, but this gets the job done for now. --- src/libsysprof-profile/sysprof-recording.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libsysprof-profile/sysprof-recording.c b/src/libsysprof-profile/sysprof-recording.c index 85be25ca..5188ab1a 100644 --- a/src/libsysprof-profile/sysprof-recording.c +++ b/src/libsysprof-profile/sysprof-recording.c @@ -226,6 +226,7 @@ sysprof_recording_stop (SysprofRecording *self) { g_return_if_fail (SYSPROF_IS_RECORDING (self)); + sysprof_recording_set_state (self, SYSPROF_RECORDING_STATE_FINISHED); } void From ce4a4200f8ae70ae1ca5e4896629e6b8be67f535 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 25 May 2023 17:24:54 -0700 Subject: [PATCH 0276/1030] libsysprof-profile: create recording and start it --- src/libsysprof-profile/sysprof-profiler.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/libsysprof-profile/sysprof-profiler.c b/src/libsysprof-profile/sysprof-profiler.c index 155cdceb..45a86a7d 100644 --- a/src/libsysprof-profile/sysprof-profiler.c +++ b/src/libsysprof-profile/sysprof-profiler.c @@ -21,6 +21,7 @@ #include "config.h" #include "sysprof-profiler.h" +#include "sysprof-recording-private.h" struct _SysprofProfiler { @@ -106,6 +107,7 @@ sysprof_profiler_record_async (SysprofProfiler *self, GAsyncReadyCallback callback, gpointer user_data) { + g_autoptr(SysprofRecording) recording = NULL; g_autoptr(GTask) task = NULL; g_return_if_fail (SYSPROF_IS_PROFILER (self)); @@ -115,7 +117,13 @@ sysprof_profiler_record_async (SysprofProfiler *self, task = g_task_new (self, cancellable, callback, user_data); g_task_set_source_tag (task, sysprof_profiler_record_async); + recording = _sysprof_recording_new (writer, + (SysprofInstrument **)self->instruments->pdata, + self->instruments->len); + g_task_return_pointer (task, g_object_ref (recording), g_object_unref); + + _sysprof_recording_start (recording); } /** From 731841a52397ad6a02d1cc4cb04e71083c6da35e Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 25 May 2023 17:25:13 -0700 Subject: [PATCH 0277/1030] libsysprof-profile: wait for recording and seutp ctrl+c --- src/libsysprof-profile/tests/test-profiler.c | 50 ++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/src/libsysprof-profile/tests/test-profiler.c b/src/libsysprof-profile/tests/test-profiler.c index 2a6fb469..6624762c 100644 --- a/src/libsysprof-profile/tests/test-profiler.c +++ b/src/libsysprof-profile/tests/test-profiler.c @@ -18,15 +18,34 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ +#include + #include static GMainLoop *main_loop; static char *capture_file; +static SysprofRecording *active_recording; static const GOptionEntry entries[] = { { "capture", 'c', 0, G_OPTION_ARG_FILENAME, &capture_file, "The file to capture into", "CAPTURE" }, { 0 } }; +static void +wait_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + SysprofRecording *recording = (SysprofRecording *)object; + g_autoptr(GError) error = NULL; + gboolean r; + + r = sysprof_recording_wait_finish (recording, result, &error); + g_assert_no_error (error); + g_assert_true (r); + + g_main_loop_quit (main_loop); +} + static void record_cb (GObject *object, GAsyncResult *result, @@ -38,6 +57,34 @@ record_cb (GObject *object, g_assert_no_error (error); g_assert_nonnull (recording); g_assert_true (SYSPROF_IS_RECORDING (recording)); + + sysprof_recording_wait_async (recording, NULL, wait_cb, NULL); + + active_recording = recording; +} + +static gboolean +sigint_handler (gpointer user_data) +{ + static int count; + + if (count >= 2) + { + g_main_loop_quit (main_loop); + return G_SOURCE_REMOVE; + } + + g_printerr ("\n"); + + if (count == 0) + { + g_printerr ("%s\n", "Stopping profiler. Press twice more ^C to force exit."); + sysprof_recording_stop (active_recording); + } + + count++; + + return G_SOURCE_CONTINUE; } int @@ -68,6 +115,9 @@ main (int argc, sysprof_profiler_record_async (profiler, writer, NULL, record_cb, NULL); + g_unix_signal_add (SIGINT, sigint_handler, main_loop); + g_unix_signal_add (SIGTERM, sigint_handler, main_loop); + g_main_loop_run (main_loop); g_clear_pointer (&writer, sysprof_capture_writer_unref); From 97128b4185c73c22d79e93dc8bee0e328aad0355 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 26 May 2023 12:18:26 -0700 Subject: [PATCH 0278/1030] libsysprof-profile: transfer instrument ownership --- src/libsysprof-profile/sysprof-profiler.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/libsysprof-profile/sysprof-profiler.c b/src/libsysprof-profile/sysprof-profiler.c index 45a86a7d..a0639f41 100644 --- a/src/libsysprof-profile/sysprof-profiler.c +++ b/src/libsysprof-profile/sysprof-profiler.c @@ -90,6 +90,16 @@ sysprof_profiler_new (void) return g_object_new (SYSPROF_TYPE_PROFILER, NULL); } +/** + * sysprof_profiler_add_instrument: + * @self: a #SysprofProfiler + * @instrument: (transfer full): a #SysprofInstrument + * + * Adds @instrument to @profiler. + * + * When the recording session is started, @instrument will be directed to + * capture data into the destination capture file. + */ void sysprof_profiler_add_instrument (SysprofProfiler *self, SysprofInstrument *instrument) @@ -97,7 +107,7 @@ sysprof_profiler_add_instrument (SysprofProfiler *self, g_return_if_fail (SYSPROF_IS_PROFILER (self)); g_return_if_fail (SYSPROF_IS_INSTRUMENT (instrument)); - g_ptr_array_add (self->instruments, g_object_ref (instrument)); + g_ptr_array_add (self->instruments, instrument); } void From 11aa39f151e9bc6b640b728673b340491786f13f Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 26 May 2023 12:18:59 -0700 Subject: [PATCH 0279/1030] libsysprof-profile: add scaffolding for controlfd source This is internal to the profile and will always be active when running subprocesses with Sysprof. --- src/libsysprof-profile/meson.build | 1 + .../sysprof-controlfd-instrument-private.h | 33 +++++++++++ .../sysprof-controlfd-instrument.c | 57 +++++++++++++++++++ src/libsysprof-profile/sysprof-profiler.c | 3 + 4 files changed, 94 insertions(+) create mode 100644 src/libsysprof-profile/sysprof-controlfd-instrument-private.h create mode 100644 src/libsysprof-profile/sysprof-controlfd-instrument.c diff --git a/src/libsysprof-profile/meson.build b/src/libsysprof-profile/meson.build index 5ce52170..d1fcc752 100644 --- a/src/libsysprof-profile/meson.build +++ b/src/libsysprof-profile/meson.build @@ -5,6 +5,7 @@ libsysprof_profile_public_sources = [ ] libsysprof_profile_private_sources = [ + 'sysprof-controlfd-instrument.c', ] libsysprof_profile_public_headers = [ diff --git a/src/libsysprof-profile/sysprof-controlfd-instrument-private.h b/src/libsysprof-profile/sysprof-controlfd-instrument-private.h new file mode 100644 index 00000000..23352b3c --- /dev/null +++ b/src/libsysprof-profile/sysprof-controlfd-instrument-private.h @@ -0,0 +1,33 @@ +/* sysprof-controlfd-instrument-private.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-instrument-private.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_CONTROLFD_INSTRUMENT (sysprof_controlfd_instrument_get_type()) + +G_DECLARE_FINAL_TYPE (SysprofControlfdInstrument, sysprof_controlfd_instrument, SYSPROF, CONTROLFD_INSTRUMENT, SysprofInstrument) + +SysprofInstrument *_sysprof_controlfd_instrument_new (void); + +G_END_DECLS diff --git a/src/libsysprof-profile/sysprof-controlfd-instrument.c b/src/libsysprof-profile/sysprof-controlfd-instrument.c new file mode 100644 index 00000000..e83f79b5 --- /dev/null +++ b/src/libsysprof-profile/sysprof-controlfd-instrument.c @@ -0,0 +1,57 @@ +/* sysprof-controlfd-instrument.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-controlfd-instrument-private.h" + +struct _SysprofControlfdInstrument +{ + SysprofInstrument parent_instance; +}; + +G_DEFINE_FINAL_TYPE (SysprofControlfdInstrument, sysprof_controlfd_instrument, SYSPROF_TYPE_INSTRUMENT) + +static void +sysprof_controlfd_instrument_finalize (GObject *object) +{ + SysprofControlfdInstrument *self = (SysprofControlfdInstrument *)object; + + G_OBJECT_CLASS (sysprof_controlfd_instrument_parent_class)->finalize (object); +} + +static void +sysprof_controlfd_instrument_class_init (SysprofControlfdInstrumentClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = sysprof_controlfd_instrument_finalize; +} + +static void +sysprof_controlfd_instrument_init (SysprofControlfdInstrument *self) +{ +} + +SysprofInstrument * +_sysprof_controlfd_instrument_new (void) +{ + return g_object_new (SYSPROF_TYPE_CONTROLFD_INSTRUMENT, NULL); +} diff --git a/src/libsysprof-profile/sysprof-profiler.c b/src/libsysprof-profile/sysprof-profiler.c index a0639f41..6f12a118 100644 --- a/src/libsysprof-profile/sysprof-profiler.c +++ b/src/libsysprof-profile/sysprof-profiler.c @@ -20,6 +20,7 @@ #include "config.h" +#include "sysprof-controlfd-instrument-private.h" #include "sysprof-profiler.h" #include "sysprof-recording-private.h" @@ -82,6 +83,8 @@ static void sysprof_profiler_init (SysprofProfiler *self) { self->instruments = g_ptr_array_new_with_free_func (g_object_unref); + + sysprof_profiler_add_instrument (self, _sysprof_controlfd_instrument_new ()); } SysprofProfiler * From d1bcf939220a1d9652ef26246b0eb5c92bec978c Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 26 May 2023 12:20:55 -0700 Subject: [PATCH 0280/1030] build: add libdex-1 requirement for libsysprof-profile Being able to manage asynchronous operations with libdex will drastically simplify how we implement the profiler and instruments. We may eventually do the same with libsysprof-analyze to parallelize some operations. --- meson.build | 2 ++ org.gnome.Sysprof.Devel.json | 19 +++++++++++++++++++ src/libsysprof-profile/meson.build | 1 + 3 files changed, 22 insertions(+) diff --git a/meson.build b/meson.build index dfe65e1b..3d330d58 100644 --- a/meson.build +++ b/meson.build @@ -46,10 +46,12 @@ need_libsysprof_capture = true need_libsysprof_profile = get_option('libsysprof') or get_option('agent') need_libsysprof_analyze = get_option('libsysprof') or get_option('agent') +dex_req = '0.2' glib_req = '2.76.0' gtk_req = '4.10' polkit_req = '0.105' +dex_req_version = '>= @0@'.format(dex_req) glib_req_version = '>= @0@'.format(glib_req) gtk_req_version = '>= @0@'.format(gtk_req) polkit_req_version = '>= @0@'.format(polkit_req) diff --git a/org.gnome.Sysprof.Devel.json b/org.gnome.Sysprof.Devel.json index cf24683f..1b172493 100644 --- a/org.gnome.Sysprof.Devel.json +++ b/org.gnome.Sysprof.Devel.json @@ -98,6 +98,25 @@ } ] }, + { + "name" : "libdex", + "buildsystem" : "meson", + "config-opts" : [ + "--buildtype=debugoptimized", + "-Ddocs=false", + "-Dintrospection=enabled", + "-Dexamples=false", + "-Dtests=false", + "-Dsysprof=false" + ], + "sources" : [ + { + "type" : "git", + "url" : "https://gitlab.gnome.org/chergert/libdex.git", + "branch" : "main" + } + ] + }, { "name" : "sysprof", "config-opts" : [ diff --git a/src/libsysprof-profile/meson.build b/src/libsysprof-profile/meson.build index d1fcc752..c3dd0310 100644 --- a/src/libsysprof-profile/meson.build +++ b/src/libsysprof-profile/meson.build @@ -18,6 +18,7 @@ libsysprof_profile_public_headers = [ libsysprof_profile_deps = [ dependency('gio-2.0', version: glib_req_version), + dependency('libdex-1', version: dex_req_version), libsysprof_capture_dep, ] From fac12d657a5f46b431ba92b3b44f336d03abac19 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 26 May 2023 13:20:24 -0700 Subject: [PATCH 0281/1030] libsysprof-profile: implement polkit checking with libdex Also, I no longer wish to try to maintain support for no-polkit. If there is truly a system where that is something we want to support, we can bring back the shims as a compile time alternative. --- src/libsysprof-profile/meson.build | 2 + .../sysprof-polkit-private.h | 34 ++++ src/libsysprof-profile/sysprof-polkit.c | 181 ++++++++++++++++++ 3 files changed, 217 insertions(+) create mode 100644 src/libsysprof-profile/sysprof-polkit-private.h create mode 100644 src/libsysprof-profile/sysprof-polkit.c diff --git a/src/libsysprof-profile/meson.build b/src/libsysprof-profile/meson.build index c3dd0310..66c7bc04 100644 --- a/src/libsysprof-profile/meson.build +++ b/src/libsysprof-profile/meson.build @@ -6,6 +6,7 @@ libsysprof_profile_public_sources = [ libsysprof_profile_private_sources = [ 'sysprof-controlfd-instrument.c', + 'sysprof-polkit.c', ] libsysprof_profile_public_headers = [ @@ -19,6 +20,7 @@ libsysprof_profile_public_headers = [ libsysprof_profile_deps = [ dependency('gio-2.0', version: glib_req_version), dependency('libdex-1', version: dex_req_version), + dependency('polkit-gobject-1', version: polkit_req_version), libsysprof_capture_dep, ] diff --git a/src/libsysprof-profile/sysprof-polkit-private.h b/src/libsysprof-profile/sysprof-polkit-private.h new file mode 100644 index 00000000..d5af505d --- /dev/null +++ b/src/libsysprof-profile/sysprof-polkit-private.h @@ -0,0 +1,34 @@ +/* sysprof-polkit-private.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include +#include +#include + +G_BEGIN_DECLS + +DexFuture *_sysprof_polkit_authorize (GDBusConnection *connection, + const char *policy, + PolkitDetails *details, + gboolean allow_user_interaction); + +G_END_DECLS diff --git a/src/libsysprof-profile/sysprof-polkit.c b/src/libsysprof-profile/sysprof-polkit.c new file mode 100644 index 00000000..c84cb31d --- /dev/null +++ b/src/libsysprof-profile/sysprof-polkit.c @@ -0,0 +1,181 @@ +/* sysprof-polkit.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-polkit-private.h" + +static void +sysprof_polkit_authority_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + g_autoptr(PolkitAuthority) authority = NULL; + g_autoptr(DexPromise) promise = user_data; + g_autoptr(GError) error = NULL; + + g_assert (G_IS_ASYNC_RESULT (result)); + g_assert (DEX_IS_PROMISE (promise)); + + if (!(authority = polkit_authority_get_finish (result, &error))) + dex_promise_reject (promise, g_steal_pointer (&error)); + else + dex_promise_resolve_object (promise, g_steal_pointer (&authority)); +} + +static DexFuture * +_sysprof_polkit_authority (void) +{ + DexPromise *promise = dex_promise_new (); + + polkit_authority_get_async (dex_promise_get_cancellable (DEX_PROMISE (promise)), + sysprof_polkit_authority_cb, + dex_ref (promise)); + + return DEX_FUTURE (promise); +} + +static void +sysprof_polkit_check_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + PolkitAuthority *authority = (PolkitAuthority *)object; + g_autoptr(PolkitAuthorizationResult) res = NULL; + g_autoptr(DexPromise) promise = user_data; + g_autoptr(GError) error = NULL; + + g_assert (POLKIT_IS_AUTHORITY (authority)); + g_assert (G_IS_ASYNC_RESULT (result)); + g_assert (DEX_IS_PROMISE (promise)); + + if (!(res = polkit_authority_check_authorization_finish (authority, result, &error))) + dex_promise_reject (promise, g_steal_pointer (&error)); + else if (!polkit_authorization_result_get_is_authorized (res)) + dex_promise_reject (promise, + g_error_new (G_DBUS_ERROR, + G_DBUS_ERROR_AUTH_FAILED, + "Failed to authorize user credentials")); + else + dex_promise_resolve_boolean (promise, TRUE); +} + +static DexFuture * +_sysprof_polkit_check (PolkitAuthority *authority, + PolkitSubject *subject, + const char *policy, + PolkitDetails *details, + PolkitCheckAuthorizationFlags flags) +{ + DexPromise *promise = dex_promise_new (); + + polkit_authority_check_authorization (authority, + subject, + policy, + details, + flags, + dex_promise_get_cancellable (DEX_PROMISE (promise)), + sysprof_polkit_check_cb, + dex_ref (promise)); + + return DEX_FUTURE (promise); +} + +typedef struct _Authorize +{ + GDBusConnection *connection; + char *policy; + PolkitDetails *details; + guint allow_user_interaction : 1; +} Authorize; + +static void +authorize_free (gpointer data) +{ + Authorize *auth = data; + + g_clear_pointer (&auth->policy, g_free); + g_clear_object (&auth->connection); + g_clear_object (&auth->details); + g_free (auth); +} + +static Authorize * +authorize_new (GDBusConnection *connection, + const char *policy, + PolkitDetails *details, + gboolean allow_user_interaction) +{ + Authorize *auth; + + auth = g_new0 (Authorize, 1); + g_set_object (&auth->connection, connection); + g_set_object (&auth->details, details); + auth->policy = g_strdup (policy); + auth->allow_user_interaction = !!allow_user_interaction; + + return auth; +} + +static DexFuture * +authorize_fiber (gpointer data) +{ + g_autoptr(PolkitAuthority) authority = NULL; + g_autoptr(PolkitSubject) subject = NULL; + g_autoptr(GError) error = NULL; + const char *bus_name; + Authorize *auth = data; + guint flags = 0; + + g_assert (auth != NULL); + g_assert (G_IS_DBUS_CONNECTION (auth->connection)); + g_assert (!auth->details || POLKIT_IS_DETAILS (auth->details)); + g_assert (auth->policy != NULL); + + bus_name = g_dbus_connection_get_unique_name (auth->connection); + subject = polkit_system_bus_name_new (bus_name); + + if (!(authority = dex_await_object (_sysprof_polkit_authority (), &error))) + return dex_future_new_for_error (g_steal_pointer (&error)); + + if (auth->allow_user_interaction) + flags |= POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION; + + if (!dex_await_boolean (_sysprof_polkit_check (authority, subject, auth->policy, auth->details, flags), &error)) + return dex_future_new_for_error (g_steal_pointer (&error)); + + return dex_future_new_for_boolean (TRUE); +} + +DexFuture * +_sysprof_polkit_authorize (GDBusConnection *connection, + const char *policy, + PolkitDetails *details, + gboolean allow_user_interaction) +{ + g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL); + g_return_val_if_fail (policy != NULL, NULL); + g_return_val_if_fail (!details || POLKIT_IS_DETAILS (details), NULL); + + return dex_scheduler_spawn (NULL, 0, + authorize_fiber, + authorize_new (connection, policy, details, allow_user_interaction), + authorize_free); +} From b4e6f7c91506c41e38847b60512d4865209630ad Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 26 May 2023 15:05:29 -0700 Subject: [PATCH 0282/1030] libsysprof-profile: add recording session fiber Setup instrument policy, preparation, etc. --- .../sysprof-instrument-private.h | 12 +- src/libsysprof-profile/sysprof-instrument.c | 106 ++++++++- src/libsysprof-profile/sysprof-recording.c | 212 +++++++----------- src/libsysprof-profile/sysprof-recording.h | 9 +- src/libsysprof-profile/tests/test-profiler.c | 2 +- 5 files changed, 208 insertions(+), 133 deletions(-) diff --git a/src/libsysprof-profile/sysprof-instrument-private.h b/src/libsysprof-profile/sysprof-instrument-private.h index bc3b45e2..431cdbc7 100644 --- a/src/libsysprof-profile/sysprof-instrument-private.h +++ b/src/libsysprof-profile/sysprof-instrument-private.h @@ -20,7 +20,10 @@ #pragma once +#include + #include "sysprof-instrument.h" +#include "sysprof-recording.h" G_BEGIN_DECLS @@ -35,9 +38,14 @@ struct _SysprofInstrumentClass { GObjectClass parent_class; - char **(*list_required_policy) (SysprofInstrument *self); + char **(*list_required_policy) (SysprofInstrument *self); + DexFuture *(*prepare) (SysprofInstrument *self, + SysprofRecording *recording); }; -char **_sysprof_instrument_list_required_policy (SysprofInstrument *self); +DexFuture * _sysprof_instruments_acquire_policy (GPtrArray *instruments, + SysprofRecording *recording); +DexFuture *_sysprof_instruments_prepare (GPtrArray *instruments, + SysprofRecording *recording); G_END_DECLS diff --git a/src/libsysprof-profile/sysprof-instrument.c b/src/libsysprof-profile/sysprof-instrument.c index baaae8b7..c8c139c5 100644 --- a/src/libsysprof-profile/sysprof-instrument.c +++ b/src/libsysprof-profile/sysprof-instrument.c @@ -21,6 +21,7 @@ #include "config.h" #include "sysprof-instrument-private.h" +#include "sysprof-polkit-private.h" G_DEFINE_ABSTRACT_TYPE (SysprofInstrument, sysprof_instrument, G_TYPE_OBJECT) @@ -30,10 +31,18 @@ sysprof_instrument_real_list_required_policy (SysprofInstrument *self) return NULL; } +static DexFuture * +sysprof_instrument_real_prepare (SysprofInstrument *instrument, + SysprofRecording *recording) +{ + return dex_future_new_for_boolean (TRUE); +} + static void sysprof_instrument_class_init (SysprofInstrumentClass *klass) { klass->list_required_policy = sysprof_instrument_real_list_required_policy; + klass->prepare = sysprof_instrument_real_prepare; } static void @@ -41,10 +50,99 @@ sysprof_instrument_init (SysprofInstrument *self) { } -char ** -_sysprof_instrument_list_required_policy (SysprofInstrument *self) +static char ** +_sysprof_instruments_list_required_policy (GPtrArray *instruments) { - g_return_val_if_fail (SYSPROF_IS_INSTRUMENT (self), NULL); + g_autoptr(GPtrArray) all_policy = NULL; - return SYSPROF_INSTRUMENT_GET_CLASS (self)->list_required_policy (self); + g_return_val_if_fail (instruments != NULL, NULL); + + all_policy = g_ptr_array_new_null_terminated (0, g_free, TRUE); + + for (guint i = 0; i > instruments->len; i++) + { + SysprofInstrument *instrument = g_ptr_array_index (instruments, i); + g_auto(GStrv) policy = SYSPROF_INSTRUMENT_GET_CLASS (instrument)->list_required_policy (instrument); + + if (policy == NULL) + continue; + + for (guint j = 0; policy[j]; j++) + { + gboolean found = FALSE; + + for (guint k = 0; !found && k < all_policy->len; k++) + found = strcmp (policy[j], g_ptr_array_index (all_policy, k)) == 0; + + if (!found) + g_ptr_array_add (all_policy, g_strdup (policy[j])); + } + } + + if (all_policy->len == 0) + return NULL; + + return (char **)g_ptr_array_free (g_steal_pointer (&all_policy), FALSE); +} + +DexFuture * +_sysprof_instruments_acquire_policy (GPtrArray *instruments, + SysprofRecording *recording) +{ + g_autoptr(GDBusConnection) connection = NULL; + g_autoptr(PolkitDetails) details = NULL; + g_autoptr(GError) error = NULL; + g_auto(GStrv) required_policy = NULL; + + g_return_val_if_fail (instruments != NULL, NULL); + g_return_val_if_fail (SYSPROF_IS_RECORDING (recording), NULL); + + /* Ensure we have access to the System D-Bus so that we can get + * access to sysprofd for system information. + */ + if (!(connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error))) + return dex_future_new_for_error (g_steal_pointer (&error)); + + /* First ensure that all our required policy have been acquired on + * the bus so that we don't need to individually acquire them from + * each of the instruments. + */ + if ((required_policy = _sysprof_instruments_list_required_policy (instruments))) + { + for (guint i = 0; required_policy[i]; i++) + { + if (!dex_await_boolean (_sysprof_polkit_authorize (connection, + required_policy[i], + details, + TRUE), &error)) + return dex_future_new_for_error (g_steal_pointer (&error)); + } + } + + return dex_future_new_for_boolean (TRUE); +} + +DexFuture * +_sysprof_instruments_prepare (GPtrArray *instruments, + SysprofRecording *recording) +{ + g_autoptr(GPtrArray) futures = NULL; + + g_return_val_if_fail (instruments != NULL, NULL); + g_return_val_if_fail (SYSPROF_IS_RECORDING (recording), NULL); + + futures = g_ptr_array_new_with_free_func (dex_unref); + + for (guint i = 0; i < instruments->len; i++) + { + SysprofInstrument *instrument = g_ptr_array_index (instruments, i); + + g_ptr_array_add (futures, + SYSPROF_INSTRUMENT_GET_CLASS (instrument)->prepare (instrument, recording)); + } + + if (futures->len == 0) + return dex_future_new_for_boolean (TRUE); + + return dex_future_allv ((DexFuture **)futures->pdata, futures->len); } diff --git a/src/libsysprof-profile/sysprof-recording.c b/src/libsysprof-profile/sysprof-recording.c index 5188ab1a..20c427dd 100644 --- a/src/libsysprof-profile/sysprof-recording.c +++ b/src/libsysprof-profile/sysprof-recording.c @@ -20,18 +20,16 @@ #include "config.h" +#include + #include "sysprof-instrument-private.h" +#include "sysprof-polkit-private.h" #include "sysprof-recording-private.h" -typedef enum _SysprofRecordingState +typedef enum _SysprofRecordingCommand { - SYSPROF_RECORDING_STATE_INITIAL, - SYSPROF_RECORDING_STATE_PRE, - SYSPROF_RECORDING_STATE_RECORD, - SYSPROF_RECORDING_STATE_POST, - SYSPROF_RECORDING_STATE_FINISHED, - SYSPROF_RECORDING_STATE_ERROR, -} SysprofRecordingState; + SYSPROF_RECORDING_COMMAND_STOP = 1, +} SysprofRecordingCommand; struct _SysprofRecording { @@ -47,13 +45,15 @@ struct _SysprofRecording /* An array of SysprofInstrument that are part of this recording */ GPtrArray *instruments; - /* Waiters contains a list of GTask to complete calls to the - * sysprof_recording_wait_async() flow. + /* A DexFiber that will complete when the recording has finished, + * been stopped, or failed. */ - GQueue waiters; + DexFuture *fiber; - /* Our current state of operation */ - SysprofRecordingState state : 3; + /* The channel is used ot send state change messages to the fiber + * from outside of the fiber. + */ + DexChannel *channel; }; enum { @@ -63,47 +63,60 @@ enum { G_DEFINE_FINAL_TYPE (SysprofRecording, sysprof_recording, G_TYPE_OBJECT) -static gboolean -sysprof_recording_state_is_terminal (SysprofRecordingState state) +static DexFuture * +sysprof_recording_fiber (gpointer user_data) { - return state == SYSPROF_RECORDING_STATE_ERROR || - state == SYSPROF_RECORDING_STATE_FINISHED; -} - -static void -sysprof_recording_set_state (SysprofRecording *self, - SysprofRecordingState state) -{ - GQueue waiters; - GTask *task; + SysprofRecording *self = user_data; + g_autoptr(GError) error = NULL; g_assert (SYSPROF_IS_RECORDING (self)); - self->state = state; + /* First ensure that all our required policy have been acquired on + * the bus so that we don't need to individually acquire them from + * each of the instruments after the recording starts. + */ + if (!dex_await (_sysprof_instruments_acquire_policy (self->instruments, self), &error)) + return dex_future_new_for_error (g_steal_pointer (&error)); - if (!sysprof_recording_state_is_terminal (state)) - return; + /* Now allow instruments to prepare for the recording */ + if (!dex_await (_sysprof_instruments_prepare (self->instruments, self), &error)) + return dex_future_new_for_error (g_steal_pointer (&error)); - waiters = self->waiters; - self->waiters = (GQueue) {NULL, NULL, 0}; - - while ((task = g_queue_pop_head (&waiters))) + /* Wait for messages on our channel until closed. */ + for (;;) { - if (state == SYSPROF_RECORDING_STATE_ERROR) - g_task_return_new_error (task, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "Recording failed"); - else - g_task_return_boolean (task, TRUE); + SysprofRecordingCommand command; - g_object_unref (task); + command = dex_await_uint (dex_channel_receive (self->channel), &error); + + switch (command) + { + default: + case SYSPROF_RECORDING_COMMAND_STOP: + goto stop_recording; + } } + +stop_recording: + + return dex_future_new_for_boolean (TRUE); } static void sysprof_recording_finalize (GObject *object) { + SysprofRecording *self = (SysprofRecording *)object; + + if (self->channel) + { + dex_channel_close_send (self->channel); + dex_clear (&self->channel); + } + + g_clear_pointer (&self->writer, sysprof_capture_writer_unref); + g_clear_pointer (&self->instruments, g_ptr_array_unref); + dex_clear (&self->fiber); + G_OBJECT_CLASS (sysprof_recording_parent_class)->finalize (object); } @@ -146,9 +159,8 @@ sysprof_recording_class_init (SysprofRecordingClass *klass) static void sysprof_recording_init (SysprofRecording *self) { + self->channel = dex_channel_new (0); self->instruments = g_ptr_array_new_with_free_func (g_object_unref); - - sysprof_recording_set_state (self, SYSPROF_RECORDING_STATE_INITIAL); } SysprofRecording * @@ -169,64 +181,45 @@ _sysprof_recording_new (SysprofCaptureWriter *writer, return self; } -static char ** -sysprof_recording_list_required_policy (SysprofRecording *self) -{ - g_autoptr(GPtrArray) all_policy = NULL; - - g_assert (SYSPROF_IS_RECORDING (self)); - - all_policy = g_ptr_array_new_null_terminated (0, g_free, TRUE); - - for (guint i = 0; i > self->instruments->len; i++) - { - SysprofInstrument *instrument = g_ptr_array_index (self->instruments, i); - g_auto(GStrv) policy = _sysprof_instrument_list_required_policy (instrument); - - if (policy == NULL) - continue; - - for (guint j = 0; policy[j]; j++) - { - gboolean found = FALSE; - - for (guint k = 0; !found && k < all_policy->len; k++) - found = strcmp (policy[j], g_ptr_array_index (all_policy, k)) == 0; - - if (!found) - g_ptr_array_add (all_policy, g_strdup (policy[j])); - } - } - - if (all_policy->len == 0) - return NULL; - - return (char **)g_ptr_array_free (all_policy, TRUE); -} - void _sysprof_recording_start (SysprofRecording *self) { - g_auto(GStrv) required_policy = NULL; - g_return_if_fail (SYSPROF_IS_RECORDING (self)); - g_return_if_fail (self->state == SYSPROF_RECORDING_STATE_INITIAL); + g_return_if_fail (self->fiber == NULL); - sysprof_recording_set_state (self, SYSPROF_RECORDING_STATE_PRE); - - if ((required_policy = sysprof_recording_list_required_policy (self))) - { - /* TODO: Query policy kit for policy */ - } + self->fiber = dex_scheduler_spawn (NULL, 0, + sysprof_recording_fiber, + g_object_ref (self), + g_object_unref); } void -sysprof_recording_stop (SysprofRecording *self) +sysprof_recording_stop_async (SysprofRecording *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { - g_return_if_fail (SYSPROF_IS_RECORDING (self)); + g_autoptr(DexAsyncResult) result = NULL; - sysprof_recording_set_state (self, SYSPROF_RECORDING_STATE_FINISHED); + g_return_if_fail (SYSPROF_IS_RECORDING (self)); + g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); + + result = dex_async_result_new (self, cancellable, callback, user_data); + dex_async_result_await (result, + dex_channel_send (self->channel, + dex_future_new_for_uint (SYSPROF_RECORDING_COMMAND_STOP))); +} + +gboolean +sysprof_recording_stop_finish (SysprofRecording *self, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (SYSPROF_IS_RECORDING (self), FALSE); + g_return_val_if_fail (DEX_IS_ASYNC_RESULT (result), FALSE); + + return dex_async_result_propagate_boolean (DEX_ASYNC_RESULT (result), error); } void @@ -235,43 +228,13 @@ sysprof_recording_wait_async (SysprofRecording *self, GAsyncReadyCallback callback, gpointer user_data) { - g_autoptr(GTask) task = NULL; + g_autoptr(DexAsyncResult) result = NULL; g_return_if_fail (SYSPROF_IS_RECORDING (self)); g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); - task = g_task_new (self, cancellable, callback, user_data); - g_task_set_source_tag (task, sysprof_recording_wait_async); - - switch (self->state) - { - case SYSPROF_RECORDING_STATE_INITIAL: - g_task_return_new_error (task, - G_IO_ERROR, - G_IO_ERROR_INVAL, - "Recording has not yet started"); - break; - - case SYSPROF_RECORDING_STATE_ERROR: - g_task_return_new_error (task, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "Recording failed"); - break; - - case SYSPROF_RECORDING_STATE_FINISHED: - g_task_return_boolean (task, TRUE); - break; - - case SYSPROF_RECORDING_STATE_PRE: - case SYSPROF_RECORDING_STATE_RECORD: - case SYSPROF_RECORDING_STATE_POST: - g_queue_push_tail (&self->waiters, g_steal_pointer (&task)); - break; - - default: - g_assert_not_reached (); - } + result = dex_async_result_new (self, cancellable, callback, user_data); + dex_async_result_await (result, dex_ref (self->fiber)); } gboolean @@ -280,8 +243,7 @@ sysprof_recording_wait_finish (SysprofRecording *self, GError **error) { g_return_val_if_fail (SYSPROF_IS_RECORDING (self), FALSE); - g_return_val_if_fail (G_IS_TASK (result), FALSE); - g_return_val_if_fail (g_task_is_valid (result, self), FALSE); + g_return_val_if_fail (DEX_IS_ASYNC_RESULT (result), FALSE); - return g_task_propagate_boolean (G_TASK (result), error); + return dex_async_result_propagate_boolean (DEX_ASYNC_RESULT (result), error); } diff --git a/src/libsysprof-profile/sysprof-recording.h b/src/libsysprof-profile/sysprof-recording.h index 1758ab80..ab1a6074 100644 --- a/src/libsysprof-profile/sysprof-recording.h +++ b/src/libsysprof-profile/sysprof-recording.h @@ -41,6 +41,13 @@ gboolean sysprof_recording_wait_finish (SysprofRecording *self, GAsyncResult *result, GError **error); SYSPROF_AVAILABLE_IN_ALL -void sysprof_recording_stop (SysprofRecording *self); +void sysprof_recording_stop_async (SysprofRecording *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +SYSPROF_AVAILABLE_IN_ALL +gboolean sysprof_recording_stop_finish (SysprofRecording *self, + GAsyncResult *result, + GError **error); G_END_DECLS diff --git a/src/libsysprof-profile/tests/test-profiler.c b/src/libsysprof-profile/tests/test-profiler.c index 6624762c..bfc499f5 100644 --- a/src/libsysprof-profile/tests/test-profiler.c +++ b/src/libsysprof-profile/tests/test-profiler.c @@ -79,7 +79,7 @@ sigint_handler (gpointer user_data) if (count == 0) { g_printerr ("%s\n", "Stopping profiler. Press twice more ^C to force exit."); - sysprof_recording_stop (active_recording); + sysprof_recording_stop_async (active_recording, NULL, NULL, NULL); } count++; From 9494b403f458583d252e0794e4762f9d31e2993f Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 26 May 2023 15:37:02 -0700 Subject: [PATCH 0283/1030] libsysprof-profile: add SysprofSpawnable This comes from libsysprof, but the goal here is to remove that library after getting this one into shape. So C&P is fine for now. --- src/libsysprof-profile/meson.build | 2 + src/libsysprof-profile/sysprof-profile.h | 1 + src/libsysprof-profile/sysprof-spawnable.c | 330 +++++++++++++++++++++ src/libsysprof-profile/sysprof-spawnable.h | 86 ++++++ 4 files changed, 419 insertions(+) create mode 100644 src/libsysprof-profile/sysprof-spawnable.c create mode 100644 src/libsysprof-profile/sysprof-spawnable.h diff --git a/src/libsysprof-profile/meson.build b/src/libsysprof-profile/meson.build index 66c7bc04..b41e59c9 100644 --- a/src/libsysprof-profile/meson.build +++ b/src/libsysprof-profile/meson.build @@ -2,6 +2,7 @@ libsysprof_profile_public_sources = [ 'sysprof-instrument.c', 'sysprof-profiler.c', 'sysprof-recording.c', + 'sysprof-spawnable.c', ] libsysprof_profile_private_sources = [ @@ -15,6 +16,7 @@ libsysprof_profile_public_headers = [ 'sysprof-instrument.h', 'sysprof-profiler.h', 'sysprof-recording.h', + 'sysprof-spawnable.h', ] libsysprof_profile_deps = [ diff --git a/src/libsysprof-profile/sysprof-profile.h b/src/libsysprof-profile/sysprof-profile.h index 9dae599c..70facde6 100644 --- a/src/libsysprof-profile/sysprof-profile.h +++ b/src/libsysprof-profile/sysprof-profile.h @@ -28,6 +28,7 @@ G_BEGIN_DECLS # include "sysprof-instrument.h" # include "sysprof-profiler.h" # include "sysprof-recording.h" +# include "sysprof-spawnable.h" #undef SYSPROF_PROFILE_INSIDE G_END_DECLS diff --git a/src/libsysprof-profile/sysprof-spawnable.c b/src/libsysprof-profile/sysprof-spawnable.c new file mode 100644 index 00000000..efe670ce --- /dev/null +++ b/src/libsysprof-profile/sysprof-spawnable.c @@ -0,0 +1,330 @@ +/* sysprof-spawnable.c + * + * Copyright 2019 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-spawnable.h" + +typedef struct +{ + gint dest_fd; + gint fd; +} FDMapping; + +struct _SysprofSpawnable +{ + GObject parent_instance; + GArray *fds; + GPtrArray *argv; + char **environ; + char *cwd; + gint next_fd; + GSubprocessFlags flags; +}; + +G_DEFINE_TYPE (SysprofSpawnable, sysprof_spawnable, G_TYPE_OBJECT) + +/** + * sysprof_spawnable_new: + * + * Create a new #SysprofSpawnable. + * + * Returns: (transfer full): a newly created #SysprofSpawnable + */ +SysprofSpawnable * +sysprof_spawnable_new (void) +{ + return g_object_new (SYSPROF_TYPE_SPAWNABLE, NULL); +} + +/** + * sysprof_spawnable_get_flags: + * @self: a #SysprofSpawnable + * + * Gets the subprocess flags for spawning. + * + * Returns: the #GSubprocessFlags bitwise-or'd + */ +GSubprocessFlags +sysprof_spawnable_get_flags (SysprofSpawnable *self) +{ + g_return_val_if_fail (SYSPROF_IS_SPAWNABLE (self), 0); + + return self->flags; +} + +/** + * sysprof_spawnable_set_flags: + * @self: a #SysprofSpawnable + * + * Set the flags to use when spawning the process. + */ +void +sysprof_spawnable_set_flags (SysprofSpawnable *self, + GSubprocessFlags flags) +{ + g_return_if_fail (SYSPROF_IS_SPAWNABLE (self)); + + self->flags = flags; +} + +static void +fd_mapping_clear (gpointer data) +{ + FDMapping *map = data; + + if (map->fd != -1) + close (map->fd); +} + +static void +sysprof_spawnable_finalize (GObject *object) +{ + SysprofSpawnable *self = (SysprofSpawnable *)object; + + g_clear_pointer (&self->fds, g_array_unref); + g_clear_pointer (&self->argv, g_ptr_array_unref); + g_clear_pointer (&self->environ, g_strfreev); + + G_OBJECT_CLASS (sysprof_spawnable_parent_class)->finalize (object); +} + +static void +sysprof_spawnable_class_init (SysprofSpawnableClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = sysprof_spawnable_finalize; +} + +static void +sysprof_spawnable_init (SysprofSpawnable *self) +{ + self->next_fd = 3; + + self->argv = g_ptr_array_new_with_free_func (g_free); + g_ptr_array_add (self->argv, NULL); + + self->fds = g_array_new (FALSE, FALSE, sizeof (FDMapping)); + g_array_set_clear_func (self->fds, fd_mapping_clear); +} + +void +sysprof_spawnable_prepend_argv (SysprofSpawnable *self, + const gchar *argv) +{ + g_return_if_fail (SYSPROF_IS_SPAWNABLE (self)); + + if (argv != NULL) + g_ptr_array_insert (self->argv, 0, g_strdup (argv)); +} + +void +sysprof_spawnable_append_argv (SysprofSpawnable *self, + const gchar *argv) +{ + gint pos; + + g_return_if_fail (SYSPROF_IS_SPAWNABLE (self)); + + if (argv == NULL) + return; + + pos = self->argv->len - 1; + g_ptr_array_add (self->argv, NULL); + g_ptr_array_index (self->argv, pos) = g_strdup (argv); +} + +void +sysprof_spawnable_append_args (SysprofSpawnable *self, + const gchar * const *args) +{ + g_return_if_fail (SYSPROF_IS_SPAWNABLE (self)); + + if (args == NULL) + return; + + for (guint i = 0; args[i]; i++) + sysprof_spawnable_append_argv (self, args[i]); +} + +const gchar * const * +sysprof_spawnable_get_argv (SysprofSpawnable *self) +{ + g_return_val_if_fail (SYSPROF_IS_SPAWNABLE (self), NULL); + + return (const gchar * const *)(gpointer)self->argv->pdata; +} + +const gchar * const * +sysprof_spawnable_get_environ (SysprofSpawnable *self) +{ + g_return_val_if_fail (SYSPROF_IS_SPAWNABLE (self), NULL); + + return (const gchar * const *)self->environ; +} + +void +sysprof_spawnable_setenv (SysprofSpawnable *self, + const gchar *key, + const gchar *value) +{ + g_return_if_fail (SYSPROF_IS_SPAWNABLE (self)); + g_return_if_fail (key != NULL); + + self->environ = g_environ_setenv (self->environ, key, value, TRUE); +} + +void +sysprof_spawnable_set_environ (SysprofSpawnable *self, + const gchar * const *environ_) +{ + g_return_if_fail (SYSPROF_IS_SPAWNABLE (self)); + + if (environ_ != (const gchar * const *)self->environ) + { + g_strfreev (self->environ); + self->environ = g_strdupv ((gchar **)environ_); + } +} + +const gchar * +sysprof_spawnable_getenv (SysprofSpawnable *self, + const gchar *key) +{ + g_return_val_if_fail (SYSPROF_IS_SPAWNABLE (self), NULL); + g_return_val_if_fail (key != NULL, NULL); + + return g_environ_getenv (self->environ, key); +} + +gint +sysprof_spawnable_take_fd (SysprofSpawnable *self, + gint fd, + gint dest_fd) +{ + FDMapping map; + + g_return_val_if_fail (SYSPROF_IS_SPAWNABLE (self), -1); + + if (dest_fd < 0) + dest_fd = self->next_fd++; + + map.dest_fd = dest_fd; + map.fd = fd; + + if (dest_fd >= self->next_fd) + self->next_fd = dest_fd + 1; + + g_array_append_val (self->fds, map); + + return dest_fd; +} + +void +sysprof_spawnable_foreach_fd (SysprofSpawnable *self, + SysprofSpawnableFDForeach foreach, + gpointer user_data) +{ + g_return_if_fail (SYSPROF_IS_SPAWNABLE (self)); + g_return_if_fail (foreach != NULL); + + for (guint i = 0; i < self->fds->len; i++) + { + const FDMapping *map = &g_array_index (self->fds, FDMapping, i); + + foreach (map->dest_fd, map->fd, user_data); + } +} + +/** + * sysprof_spawnable_set_starting_fd: + * + * Sets the next FD number to use when mapping a child FD. This helps + * in situations where the embedder knows that some lower-numbered FDs + * will be taken and therefore unknown to the spawnable. + * + * The default for this is 2. + */ +void +sysprof_spawnable_set_starting_fd (SysprofSpawnable *self, + gint starting_fd) +{ + g_return_if_fail (SYSPROF_IS_SPAWNABLE (self)); + + if (starting_fd < 0) + starting_fd = 2; + + self->next_fd = starting_fd; +} + +/** + * sysprof_spawnable_spawn: + * + * Creates a new subprocess using the configured options. + * + * Returns: (transfer full): a #GSubprocess or %NULL on failure and + * @error is set. + */ +GSubprocess * +sysprof_spawnable_spawn (SysprofSpawnable *self, + GError **error) +{ + g_autoptr(GSubprocessLauncher) launcher = NULL; + const gchar * const *argv; + + g_return_val_if_fail (SYSPROF_IS_SPAWNABLE (self), NULL); + + launcher = g_subprocess_launcher_new (self->flags); + + g_subprocess_launcher_set_environ (launcher, self->environ); + + if (self->cwd != NULL) + g_subprocess_launcher_set_cwd (launcher, self->cwd); + else + g_subprocess_launcher_set_cwd (launcher, g_get_home_dir ()); + + for (guint i = 0; i < self->fds->len; i++) + { + FDMapping *map = &g_array_index (self->fds, FDMapping, i); + + g_subprocess_launcher_take_fd (launcher, map->fd, map->dest_fd); + map->fd = -1; + } + + argv = sysprof_spawnable_get_argv (self); + + return g_subprocess_launcher_spawnv (launcher, argv, error); +} + +void +sysprof_spawnable_set_cwd (SysprofSpawnable *self, + const gchar *cwd) +{ + g_return_if_fail (SYSPROF_IS_SPAWNABLE (self)); + + if (g_strcmp0 (cwd, self->cwd) != 0) + { + g_free (self->cwd); + self->cwd = g_strdup (cwd); + } +} diff --git a/src/libsysprof-profile/sysprof-spawnable.h b/src/libsysprof-profile/sysprof-spawnable.h new file mode 100644 index 00000000..f8ad8bf7 --- /dev/null +++ b/src/libsysprof-profile/sysprof-spawnable.h @@ -0,0 +1,86 @@ +/* sysprof-spawnable.h + * + * Copyright 2019 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_SPAWNABLE (sysprof_spawnable_get_type()) + +typedef void (*SysprofSpawnableFDForeach) (gint dest_fd, + gint fd, + gpointer user_data); + +SYSPROF_AVAILABLE_IN_ALL +G_DECLARE_FINAL_TYPE (SysprofSpawnable, sysprof_spawnable, SYSPROF, SPAWNABLE, GObject) + +SYSPROF_AVAILABLE_IN_ALL +SysprofSpawnable *sysprof_spawnable_new (void); +SYSPROF_AVAILABLE_IN_ALL +void sysprof_spawnable_prepend_argv (SysprofSpawnable *self, + const gchar *argv); +SYSPROF_AVAILABLE_IN_ALL +void sysprof_spawnable_append_argv (SysprofSpawnable *self, + const gchar *argv); +SYSPROF_AVAILABLE_IN_ALL +void sysprof_spawnable_append_args (SysprofSpawnable *self, + const gchar * const *argv); +SYSPROF_AVAILABLE_IN_ALL +void sysprof_spawnable_set_cwd (SysprofSpawnable *self, + const gchar *cwd); +SYSPROF_AVAILABLE_IN_ALL +const gchar * const *sysprof_spawnable_get_argv (SysprofSpawnable *self); +SYSPROF_AVAILABLE_IN_ALL +const gchar * const *sysprof_spawnable_get_environ (SysprofSpawnable *self); +SYSPROF_AVAILABLE_IN_ALL +void sysprof_spawnable_set_environ (SysprofSpawnable *self, + const gchar * const *environ); +SYSPROF_AVAILABLE_IN_ALL +void sysprof_spawnable_setenv (SysprofSpawnable *self, + const gchar *key, + const gchar *value); +SYSPROF_AVAILABLE_IN_ALL +const gchar *sysprof_spawnable_getenv (SysprofSpawnable *self, + const gchar *key); +SYSPROF_AVAILABLE_IN_ALL +gint sysprof_spawnable_take_fd (SysprofSpawnable *self, + gint fd, + gint dest_fd); +SYSPROF_AVAILABLE_IN_ALL +void sysprof_spawnable_foreach_fd (SysprofSpawnable *self, + SysprofSpawnableFDForeach foreach, + gpointer user_data); +SYSPROF_AVAILABLE_IN_ALL +void sysprof_spawnable_set_starting_fd (SysprofSpawnable *self, + gint starting_fd); +SYSPROF_AVAILABLE_IN_ALL +GSubprocess *sysprof_spawnable_spawn (SysprofSpawnable *self, + GError **error); +SYSPROF_AVAILABLE_IN_ALL +GSubprocessFlags sysprof_spawnable_get_flags (SysprofSpawnable *self); +SYSPROF_AVAILABLE_IN_ALL +void sysprof_spawnable_set_flags (SysprofSpawnable *self, + GSubprocessFlags flags); + +G_END_DECLS From 800c32e303f3f3bab9a092c0c59453aa32b9406c Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 26 May 2023 15:37:39 -0700 Subject: [PATCH 0284/1030] libsysprof-profile: add private API to get recording spawnable This will still need to be set by the profiler eventually, but this gets the readback API in place so we can use it from instruments. --- src/libsysprof-profile/sysprof-recording-private.h | 12 +++++++----- src/libsysprof-profile/sysprof-recording.c | 13 +++++++++++++ 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/libsysprof-profile/sysprof-recording-private.h b/src/libsysprof-profile/sysprof-recording-private.h index 67852820..38f0354d 100644 --- a/src/libsysprof-profile/sysprof-recording-private.h +++ b/src/libsysprof-profile/sysprof-recording-private.h @@ -22,13 +22,15 @@ #include "sysprof-instrument.h" #include "sysprof-recording.h" +#include "sysprof-spawnable.h" G_BEGIN_DECLS -SysprofRecording *_sysprof_recording_new (SysprofCaptureWriter *writer, - SysprofInstrument **instruments, - guint n_instruments); -void _sysprof_recording_start (SysprofRecording *self); -SysprofCaptureWriter *_sysprof_recording_writer (SysprofRecording *self); +SysprofRecording *_sysprof_recording_new (SysprofCaptureWriter *writer, + SysprofInstrument **instruments, + guint n_instruments); +void _sysprof_recording_start (SysprofRecording *self); +SysprofCaptureWriter *_sysprof_recording_writer (SysprofRecording *self); +SysprofSpawnable *_sysprof_recording_get_spawnable (SysprofRecording *self); G_END_DECLS diff --git a/src/libsysprof-profile/sysprof-recording.c b/src/libsysprof-profile/sysprof-recording.c index 20c427dd..e0a753a9 100644 --- a/src/libsysprof-profile/sysprof-recording.c +++ b/src/libsysprof-profile/sysprof-recording.c @@ -35,6 +35,11 @@ struct _SysprofRecording { GObject parent_instance; + /* If we are spawning a process as part of this recording, this + * is the SysprofSpawnable used to spawn the process. + */ + SysprofSpawnable *spawnable; + /* This is where all of the instruments will write to. They are * expected to do this from the main-thread only. To work from * additional threads they need to proxy that state to the @@ -247,3 +252,11 @@ sysprof_recording_wait_finish (SysprofRecording *self, return dex_async_result_propagate_boolean (DEX_ASYNC_RESULT (result), error); } + +SysprofSpawnable * +_sysprof_recording_get_spawnable (SysprofRecording *self) +{ + g_return_val_if_fail (SYSPROF_IS_RECORDING (self), NULL); + + return self->spawnable; +} From 1e3ad3982d95db5d9e33746922d5eaa4ce416ce8 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 26 May 2023 15:41:58 -0700 Subject: [PATCH 0285/1030] libsysprof-profile: implement prepare vfunc This doesn't start reading from the input stream, but does get things into place until we actually start processing the recording. When we get a record vfunc, we will do the input processing. --- src/libsysprof-profile/meson.build | 3 + .../sysprof-controlfd-instrument.c | 100 +++++++++++++++++- 2 files changed, 102 insertions(+), 1 deletion(-) diff --git a/src/libsysprof-profile/meson.build b/src/libsysprof-profile/meson.build index b41e59c9..9d53033b 100644 --- a/src/libsysprof-profile/meson.build +++ b/src/libsysprof-profile/meson.build @@ -21,6 +21,9 @@ libsysprof_profile_public_headers = [ libsysprof_profile_deps = [ dependency('gio-2.0', version: glib_req_version), + dependency('gio-unix-2.0', + version: glib_req_version, + required: host_machine.system() != 'windows'), dependency('libdex-1', version: dex_req_version), dependency('polkit-gobject-1', version: polkit_req_version), diff --git a/src/libsysprof-profile/sysprof-controlfd-instrument.c b/src/libsysprof-profile/sysprof-controlfd-instrument.c index e83f79b5..7ab166b2 100644 --- a/src/libsysprof-profile/sysprof-controlfd-instrument.c +++ b/src/libsysprof-profile/sysprof-controlfd-instrument.c @@ -20,38 +20,136 @@ #include "config.h" +#include + +#ifdef G_OS_UNIX +# include +# include +# include +# include +# include +# include +# include +# include +#endif + #include "sysprof-controlfd-instrument-private.h" +#include "sysprof-recording-private.h" struct _SysprofControlfdInstrument { - SysprofInstrument parent_instance; + SysprofInstrument parent_instance; +#ifdef G_OS_UNIX + GUnixConnection *connection; + GArray *source_ids; + char read_buf[10]; +#endif }; G_DEFINE_FINAL_TYPE (SysprofControlfdInstrument, sysprof_controlfd_instrument, SYSPROF_TYPE_INSTRUMENT) +#ifdef G_OS_UNIX +static DexFuture * +sysprof_controlfd_instrument_prepare (SysprofInstrument *instrument, + SysprofRecording *recording) +{ + SysprofControlfdInstrument *self = (SysprofControlfdInstrument *)instrument; + SysprofSpawnable *spawnable; + g_autofree char *child_no_str = NULL; + g_autoptr(GSocketConnection) stream = NULL; + g_autoptr(GSocket) sock = NULL; + int fds[2] = {-1, -1}; + int child_no; + + g_assert (SYSPROF_IS_CONTROLFD_INSTRUMENT (self)); + g_assert (SYSPROF_IS_RECORDING (recording)); + + /* If the recording is not spawning a process, then there is + * nothing for us to do. + */ + if (!(spawnable = _sysprof_recording_get_spawnable (recording))) + goto finish; + + /* Create a socket pair to send control messages over */ + if (socketpair (AF_LOCAL, SOCK_STREAM, 0, fds) != 0) + return dex_future_new_reject (G_IO_ERROR, + G_IO_ERROR_NOT_CONNECTED, + "Failed to create socketpair"); + + /* Set FDs non-blocking so that we can use them from main + * context iteration without blocking. + */ + g_unix_set_fd_nonblocking (fds[0], TRUE, NULL); + g_unix_set_fd_nonblocking (fds[1], TRUE, NULL); + + /* @child_no is assigned the FD the child will receive. We can + * use that to set the environment vaiable of the control FD. + */ + child_no = sysprof_spawnable_take_fd (spawnable, fds[1], -1); + child_no_str = g_strdup_printf ("%d", child_no); + sysprof_spawnable_setenv (spawnable, "SYSPROF_CONTROL_FD", child_no_str); + + if (!(sock = g_socket_new_from_fd (fds[0], NULL))) + { + close (fds[0]); + return dex_future_new_reject (G_IO_ERROR, + G_IO_ERROR_NOT_CONNECTED, + "Failed to create socket from FD"); + } + + self->connection = G_UNIX_CONNECTION (g_socket_connection_factory_create_connection (sock)); + +finish: + return dex_future_new_for_boolean (TRUE); +} + +static void +remove_source_id (gpointer data) +{ + guint *id = data; + g_source_remove (*id); +} + static void sysprof_controlfd_instrument_finalize (GObject *object) { SysprofControlfdInstrument *self = (SysprofControlfdInstrument *)object; + g_clear_pointer (&self->source_ids, g_array_unref); + g_clear_object (&self->connection); + G_OBJECT_CLASS (sysprof_controlfd_instrument_parent_class)->finalize (object); } +#endif static void sysprof_controlfd_instrument_class_init (SysprofControlfdInstrumentClass *klass) { +#ifdef G_OS_UNIX GObjectClass *object_class = G_OBJECT_CLASS (klass); + SysprofInstrumentClass *instrument_class = SYSPROF_INSTRUMENT_CLASS (klass); object_class->finalize = sysprof_controlfd_instrument_finalize; + + instrument_class->prepare = sysprof_controlfd_instrument_prepare; +#endif } static void sysprof_controlfd_instrument_init (SysprofControlfdInstrument *self) { +#ifdef G_OS_UNIX + self->source_ids = g_array_new (FALSE, FALSE, sizeof (guint)); + g_array_set_clear_func (self->source_ids, remove_source_id); +#endif } SysprofInstrument * _sysprof_controlfd_instrument_new (void) { +#ifndef G_OS_UNIX + g_warning_once ("SysprofControlfdInstrument not supported on this platform"); +#endif + return g_object_new (SYSPROF_TYPE_CONTROLFD_INSTRUMENT, NULL); } From 4d54aa6a5164d1773a1cdc9488c65cb1ad3e4874 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 26 May 2023 16:05:30 -0700 Subject: [PATCH 0286/1030] libsysprof-profile: add SysprofInstrument record vfunc --- .../sysprof-instrument-private.h | 6 +++ src/libsysprof-profile/sysprof-instrument.c | 46 ++++++++++++++++++- 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/src/libsysprof-profile/sysprof-instrument-private.h b/src/libsysprof-profile/sysprof-instrument-private.h index 431cdbc7..7755a349 100644 --- a/src/libsysprof-profile/sysprof-instrument-private.h +++ b/src/libsysprof-profile/sysprof-instrument-private.h @@ -41,11 +41,17 @@ struct _SysprofInstrumentClass char **(*list_required_policy) (SysprofInstrument *self); DexFuture *(*prepare) (SysprofInstrument *self, SysprofRecording *recording); + DexFuture *(*record) (SysprofInstrument *self, + SysprofRecording *recording, + GCancellable *cancellable); }; DexFuture * _sysprof_instruments_acquire_policy (GPtrArray *instruments, SysprofRecording *recording); DexFuture *_sysprof_instruments_prepare (GPtrArray *instruments, SysprofRecording *recording); +DexFuture *_sysprof_instruments_record (GPtrArray *instruments, + SysprofRecording *recording, + GCancellable *cancellable); G_END_DECLS diff --git a/src/libsysprof-profile/sysprof-instrument.c b/src/libsysprof-profile/sysprof-instrument.c index c8c139c5..d2c1f27f 100644 --- a/src/libsysprof-profile/sysprof-instrument.c +++ b/src/libsysprof-profile/sysprof-instrument.c @@ -26,8 +26,10 @@ G_DEFINE_ABSTRACT_TYPE (SysprofInstrument, sysprof_instrument, G_TYPE_OBJECT) static char ** -sysprof_instrument_real_list_required_policy (SysprofInstrument *self) +sysprof_instrument_real_list_required_policy (SysprofInstrument *instrument) { + g_assert (SYSPROF_IS_INSTRUMENT (instrument)); + return NULL; } @@ -35,6 +37,21 @@ static DexFuture * sysprof_instrument_real_prepare (SysprofInstrument *instrument, SysprofRecording *recording) { + g_assert (SYSPROF_IS_INSTRUMENT (instrument)); + g_assert (SYSPROF_IS_RECORDING (recording)); + + return dex_future_new_for_boolean (TRUE); +} + +static DexFuture * +sysprof_instrument_real_record (SysprofInstrument *instrument, + SysprofRecording *recording, + GCancellable *cancellable) +{ + g_assert (SYSPROF_IS_INSTRUMENT (instrument)); + g_assert (SYSPROF_IS_RECORDING (recording)); + g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); + return dex_future_new_for_boolean (TRUE); } @@ -43,6 +60,7 @@ sysprof_instrument_class_init (SysprofInstrumentClass *klass) { klass->list_required_policy = sysprof_instrument_real_list_required_policy; klass->prepare = sysprof_instrument_real_prepare; + klass->record = sysprof_instrument_real_record; } static void @@ -146,3 +164,29 @@ _sysprof_instruments_prepare (GPtrArray *instruments, return dex_future_allv ((DexFuture **)futures->pdata, futures->len); } + +DexFuture * +_sysprof_instruments_record (GPtrArray *instruments, + SysprofRecording *recording, + GCancellable *cancellable) +{ + g_autoptr(GPtrArray) futures = NULL; + + g_return_val_if_fail (instruments != NULL, NULL); + g_return_val_if_fail (SYSPROF_IS_RECORDING (recording), NULL); + + futures = g_ptr_array_new_with_free_func (dex_unref); + + for (guint i = 0; i < instruments->len; i++) + { + SysprofInstrument *instrument = g_ptr_array_index (instruments, i); + + g_ptr_array_add (futures, + SYSPROF_INSTRUMENT_GET_CLASS (instrument)->record (instrument, recording, cancellable)); + } + + if (futures->len == 0) + return dex_future_new_for_boolean (TRUE); + + return dex_future_allv ((DexFuture **)futures->pdata, futures->len); +} From 8409022c5c0eed918ccae8bd8ce959009f986f63 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 26 May 2023 16:05:48 -0700 Subject: [PATCH 0287/1030] libsysprof-profile: add record step to recording/instruments --- src/libsysprof-profile/sysprof-recording.c | 42 ++++++++++++++++++---- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/src/libsysprof-profile/sysprof-recording.c b/src/libsysprof-profile/sysprof-recording.c index e0a753a9..5ebb4c56 100644 --- a/src/libsysprof-profile/sysprof-recording.c +++ b/src/libsysprof-profile/sysprof-recording.c @@ -72,10 +72,14 @@ static DexFuture * sysprof_recording_fiber (gpointer user_data) { SysprofRecording *self = user_data; + g_autoptr(GCancellable) cancellable = NULL; + g_autoptr(DexFuture) record = NULL; g_autoptr(GError) error = NULL; g_assert (SYSPROF_IS_RECORDING (self)); + cancellable = g_cancellable_new (); + /* First ensure that all our required policy have been acquired on * the bus so that we don't need to individually acquire them from * each of the instruments after the recording starts. @@ -87,22 +91,46 @@ sysprof_recording_fiber (gpointer user_data) if (!dex_await (_sysprof_instruments_prepare (self->instruments, self), &error)) return dex_future_new_for_error (g_steal_pointer (&error)); - /* Wait for messages on our channel until closed. */ + /* Ask instruments to start recording and stop if cancelled. */ + record = _sysprof_instruments_record (self->instruments, self, cancellable); + + /* Wait for messages on our channel or the recording to complete */ for (;;) { - SysprofRecordingCommand command; + g_autoptr(DexFuture) message = dex_channel_receive (self->channel); - command = dex_await_uint (dex_channel_receive (self->channel), &error); + /* Wait for either recording of all instruments to complete or a + * message from our channel with what to do next. + */ + if (!dex_await (dex_future_any (dex_ref (record), dex_ref (message), NULL), &error)) + goto stop_recording; - switch (command) + /* If record is not pending, then everything resolved/rejected */ + if (dex_future_get_status (record) != DEX_FUTURE_STATUS_PENDING) + goto stop_recording; + + /* If message resolved, then we got a command to process */ + if (dex_future_get_status (message) == DEX_FUTURE_STATUS_RESOLVED) { - default: - case SYSPROF_RECORDING_COMMAND_STOP: - goto stop_recording; + SysprofRecordingCommand command = dex_await_uint (dex_ref (message), NULL); + + switch (command) + { + default: + case SYSPROF_RECORDING_COMMAND_STOP: + goto stop_recording; + } } } stop_recording: + /* Signal cancellable so that anything lingering has a chance to be + * cleaned up, cascading into other subsystems. + */ + g_cancellable_cancel (cancellable); + + if (error != NULL) + return dex_future_new_for_error (g_steal_pointer (&error)); return dex_future_new_for_boolean (TRUE); } From 55353aa185eeb6a63257c222e409dd6a841f1851 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 26 May 2023 16:32:17 -0700 Subject: [PATCH 0288/1030] libsysprof-profile: add record vfunc scaffolding We still need to create the ring buffer and the source to stream it into the capture writer, but that will come soon. --- .../sysprof-controlfd-instrument.c | 97 +++++++++++++++++-- 1 file changed, 88 insertions(+), 9 deletions(-) diff --git a/src/libsysprof-profile/sysprof-controlfd-instrument.c b/src/libsysprof-profile/sysprof-controlfd-instrument.c index 7ab166b2..41f27235 100644 --- a/src/libsysprof-profile/sysprof-controlfd-instrument.c +++ b/src/libsysprof-profile/sysprof-controlfd-instrument.c @@ -41,7 +41,6 @@ struct _SysprofControlfdInstrument SysprofInstrument parent_instance; #ifdef G_OS_UNIX GUnixConnection *connection; - GArray *source_ids; char read_buf[10]; #endif }; @@ -83,7 +82,7 @@ sysprof_controlfd_instrument_prepare (SysprofInstrument *instrument, g_unix_set_fd_nonblocking (fds[1], TRUE, NULL); /* @child_no is assigned the FD the child will receive. We can - * use that to set the environment vaiable of the control FD. + * use that to set the environment variable of the control FD. */ child_no = sysprof_spawnable_take_fd (spawnable, fds[1], -1); child_no_str = g_strdup_printf ("%d", child_no); @@ -103,11 +102,95 @@ finish: return dex_future_new_for_boolean (TRUE); } +typedef struct _SysprofControlfdRecording +{ + GInputStream *stream; + SysprofRecording *recording; + DexFuture *cancellable; + GArray *source_ids; +} SysprofControlfdRecording; + static void -remove_source_id (gpointer data) +sysprof_controlfd_recording_free (gpointer data) +{ + SysprofControlfdRecording *state = data; + + dex_clear (&state->cancellable); + g_clear_pointer (&state->source_ids, g_array_unref); + g_clear_object (&state->recording); + g_clear_object (&state->stream); + g_free (state); +} + +static DexFuture * +sysprof_controlfd_instrument_record_fiber (gpointer user_data) +{ + SysprofControlfdRecording *state = user_data; + + g_assert (state != NULL); + g_assert (SYSPROF_IS_RECORDING (state->recording)); + g_assert (DEX_IS_CANCELLABLE (state->cancellable)); + g_assert (state->source_ids != NULL); + + for (;;) + { + g_autoptr(DexFuture) future = dex_input_stream_read_bytes (state->stream, 10, 0); + //g_autoptr(MappedRinBuffer) ring_buffer = NULL; + g_autoptr(GBytes) bytes = NULL; + g_autoptr(GError) error = NULL; + const guint8 *data; + gsize len; + + dex_await (dex_future_any (dex_ref (future), + dex_ref (state->cancellable), + NULL), + &error); + + if (error != NULL) + return dex_future_new_for_error (g_steal_pointer (&error)); + + if (!(bytes = dex_await_boxed (dex_ref (future), &error))) + return dex_future_new_for_error (g_steal_pointer (&error)); + + data = g_bytes_get_data (bytes, &len); + if (len != 10 || memcmp (data, "CreatRing\0", 10) != 0) + break; + + } + + return NULL; +} + +static void +_g_clear_source (gpointer data) { guint *id = data; - g_source_remove (*id); + + if (*id != 0) + g_source_remove (*id); +} + +static DexFuture * +sysprof_controlfd_instrument_record (SysprofInstrument *instrument, + SysprofRecording *recording, + GCancellable *cancellable) +{ + SysprofControlfdInstrument *self = (SysprofControlfdInstrument *)instrument; + SysprofControlfdRecording *state; + + g_assert (SYSPROF_IS_CONTROLFD_INSTRUMENT (self)); + g_assert (SYSPROF_IS_RECORDING (recording)); + + state = g_new0 (SysprofControlfdRecording, 1); + state->recording = g_object_ref (recording); + state->cancellable = dex_cancellable_new_from_cancellable (cancellable); + state->source_ids = g_array_new (FALSE, FALSE, sizeof (guint)); + g_array_set_clear_func (state->source_ids, _g_clear_source); + + return dex_scheduler_spawn (NULL, 0, + sysprof_controlfd_instrument_record_fiber, + state, + sysprof_controlfd_recording_free); } static void @@ -115,7 +198,6 @@ sysprof_controlfd_instrument_finalize (GObject *object) { SysprofControlfdInstrument *self = (SysprofControlfdInstrument *)object; - g_clear_pointer (&self->source_ids, g_array_unref); g_clear_object (&self->connection); G_OBJECT_CLASS (sysprof_controlfd_instrument_parent_class)->finalize (object); @@ -132,16 +214,13 @@ sysprof_controlfd_instrument_class_init (SysprofControlfdInstrumentClass *klass) object_class->finalize = sysprof_controlfd_instrument_finalize; instrument_class->prepare = sysprof_controlfd_instrument_prepare; + instrument_class->record = sysprof_controlfd_instrument_record; #endif } static void sysprof_controlfd_instrument_init (SysprofControlfdInstrument *self) { -#ifdef G_OS_UNIX - self->source_ids = g_array_new (FALSE, FALSE, sizeof (guint)); - g_array_set_clear_func (self->source_ids, remove_source_id); -#endif } SysprofInstrument * From 38dbf0dc9542688740b29fc6717c422d47d707a4 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 26 May 2023 16:46:44 -0700 Subject: [PATCH 0289/1030] build: add mapped_ring_buffer_sources for inclusion We use this in various places but it relies on some static API in the libsysprof-capture. Make it available to the other libraries. --- src/libsysprof-capture/meson.build | 8 ++++++-- src/libsysprof-profile/meson.build | 5 +++-- src/libsysprof/meson.build | 4 +++- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/libsysprof-capture/meson.build b/src/libsysprof-capture/meson.build index f548f827..cea56e62 100644 --- a/src/libsysprof-capture/meson.build +++ b/src/libsysprof-capture/meson.build @@ -19,8 +19,11 @@ if not meson.is_subproject() install_headers(libsysprof_capture_headers, subdir: sysprof_header_subdir) endif -libsysprof_capture_sources = files([ +mapped_ring_buffer_sources = files([ 'mapped-ring-buffer.c', +]) + +libsysprof_capture_sources = files([ 'sysprof-address.c', 'sysprof-capture-condition.c', 'sysprof-capture-cursor.c', @@ -47,7 +50,8 @@ libsysprof_capture_deps = [ libsysprof_capture = static_library( 'sysprof-capture-@0@'.format(libsysprof_api_version), - libsysprof_capture_sources, + (libsysprof_capture_sources + + mapped_ring_buffer_sources), dependencies: libsysprof_capture_deps, c_args: [ '-DSYSPROF_CAPTURE_COMPILATION' ], diff --git a/src/libsysprof-profile/meson.build b/src/libsysprof-profile/meson.build index 9d53033b..6a1ef5d7 100644 --- a/src/libsysprof-profile/meson.build +++ b/src/libsysprof-profile/meson.build @@ -32,8 +32,9 @@ libsysprof_profile_deps = [ libsysprof_profile_static = static_library( 'sysprof-profile-@0@'.format(soname_major_version), - libsysprof_profile_public_sources + - libsysprof_profile_private_sources, + (libsysprof_profile_public_sources + + libsysprof_profile_private_sources + + mapped_ring_buffer_sources), include_directories: [include_directories('.'), ipc_include_dirs, diff --git a/src/libsysprof/meson.build b/src/libsysprof/meson.build index a730ef11..fa0f5263 100644 --- a/src/libsysprof/meson.build +++ b/src/libsysprof/meson.build @@ -150,7 +150,9 @@ endif libsysprof_static = static_library( 'sysprof', - libsysprof_public_sources + libsysprof_private_sources, + (libsysprof_public_sources + + libsysprof_private_sources + + mapped_ring_buffer_sources), include_directories: [include_directories('.'), ipc_include_dirs, From e4ec04812df7976a096710d4daba6cacc11c9fb9 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 26 May 2023 16:48:26 -0700 Subject: [PATCH 0290/1030] libsysprof-profile: bring over mapped ring buffer source --- .../mapped-ring-buffer-source-private.h | 41 ++++++ .../mapped-ring-buffer-source.c | 119 ++++++++++++++++++ src/libsysprof-profile/meson.build | 1 + 3 files changed, 161 insertions(+) create mode 100644 src/libsysprof-profile/mapped-ring-buffer-source-private.h create mode 100644 src/libsysprof-profile/mapped-ring-buffer-source.c diff --git a/src/libsysprof-profile/mapped-ring-buffer-source-private.h b/src/libsysprof-profile/mapped-ring-buffer-source-private.h new file mode 100644 index 00000000..1867e6ff --- /dev/null +++ b/src/libsysprof-profile/mapped-ring-buffer-source-private.h @@ -0,0 +1,41 @@ +/* mapped-ring-buffer-source-private.h + * + * Copyright 2020 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 "mapped-ring-buffer.h" + +G_BEGIN_DECLS + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (MappedRingBuffer, mapped_ring_buffer_unref) + +G_GNUC_INTERNAL +guint mapped_ring_buffer_create_source (MappedRingBuffer *self, + MappedRingBufferCallback callback, + gpointer user_data); +G_GNUC_INTERNAL +guint mapped_ring_buffer_create_source_full (MappedRingBuffer *self, + MappedRingBufferCallback callback, + gpointer user_data, + GDestroyNotify destroy); + +G_END_DECLS diff --git a/src/libsysprof-profile/mapped-ring-buffer-source.c b/src/libsysprof-profile/mapped-ring-buffer-source.c new file mode 100644 index 00000000..a0a939e7 --- /dev/null +++ b/src/libsysprof-profile/mapped-ring-buffer-source.c @@ -0,0 +1,119 @@ +/* mapped-ring-buffer-source.c + * + * Copyright 2020 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 "mapped-ring-buffer-source-private.h" + +typedef struct _MappedRingSource +{ + GSource source; + MappedRingBuffer *buffer; +} MappedRingSource; + +static gboolean +mapped_ring_source_dispatch (GSource *source, + GSourceFunc callback, + gpointer user_data) +{ + MappedRingSource *real_source = (MappedRingSource *)source; + + g_assert (source != NULL); + + return mapped_ring_buffer_drain (real_source->buffer, + (MappedRingBufferCallback)callback, + user_data); +} + +static void +mapped_ring_source_finalize (GSource *source) +{ + MappedRingSource *real_source = (MappedRingSource *)source; + + if (real_source != NULL) + g_clear_pointer (&real_source->buffer, mapped_ring_buffer_unref); +} + +static gboolean +mapped_ring_source_check (GSource *source) +{ + MappedRingSource *real_source = (MappedRingSource *)source; + + g_assert (real_source != NULL); + g_assert (real_source->buffer != NULL); + + return !mapped_ring_buffer_is_empty (real_source->buffer); +} + +static gboolean +mapped_ring_source_prepare (GSource *source, + gint *timeout_) +{ + MappedRingSource *real_source = (MappedRingSource *)source; + + g_assert (real_source != NULL); + g_assert (real_source->buffer != NULL); + + if (!mapped_ring_buffer_is_empty (real_source->buffer)) + return TRUE; + + *timeout_ = 5; + + return FALSE; +} + +static GSourceFuncs mapped_ring_source_funcs = { + .prepare = mapped_ring_source_prepare, + .check = mapped_ring_source_check, + .dispatch = mapped_ring_source_dispatch, + .finalize = mapped_ring_source_finalize, +}; + +guint +mapped_ring_buffer_create_source_full (MappedRingBuffer *self, + MappedRingBufferCallback source_func, + gpointer user_data, + GDestroyNotify destroy) +{ + MappedRingSource *source; + guint ret; + + g_return_val_if_fail (self != NULL, 0); + g_return_val_if_fail (source_func != NULL, 0); + + source = (MappedRingSource *)g_source_new (&mapped_ring_source_funcs, sizeof (MappedRingSource)); + source->buffer = mapped_ring_buffer_ref (self); + g_source_set_callback ((GSource *)source, (GSourceFunc)source_func, user_data, destroy); + g_source_set_name ((GSource *)source, "MappedRingSource"); + ret = g_source_attach ((GSource *)source, g_main_context_default ()); + g_source_unref ((GSource *)source); + + return ret; +} + +guint +mapped_ring_buffer_create_source (MappedRingBuffer *self, + MappedRingBufferCallback source_func, + gpointer user_data) +{ + return mapped_ring_buffer_create_source_full (self, source_func, user_data, NULL); +} diff --git a/src/libsysprof-profile/meson.build b/src/libsysprof-profile/meson.build index 6a1ef5d7..56f02cd6 100644 --- a/src/libsysprof-profile/meson.build +++ b/src/libsysprof-profile/meson.build @@ -6,6 +6,7 @@ libsysprof_profile_public_sources = [ ] libsysprof_profile_private_sources = [ + 'mapped-ring-buffer-source.c', 'sysprof-controlfd-instrument.c', 'sysprof-polkit.c', ] From 7c98f782c04e13ca50d780a90bb70c88f2a4ac1e Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 26 May 2023 17:07:16 -0700 Subject: [PATCH 0291/1030] libsysprof-profile: add getter for capture writer --- src/libsysprof-profile/sysprof-recording.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/libsysprof-profile/sysprof-recording.c b/src/libsysprof-profile/sysprof-recording.c index 5ebb4c56..1dc4885d 100644 --- a/src/libsysprof-profile/sysprof-recording.c +++ b/src/libsysprof-profile/sysprof-recording.c @@ -288,3 +288,11 @@ _sysprof_recording_get_spawnable (SysprofRecording *self) return self->spawnable; } + +SysprofCaptureWriter * +_sysprof_recording_writer (SysprofRecording *self) +{ + g_return_val_if_fail (SYSPROF_IS_RECORDING (self), NULL); + + return self->writer; +} From 62071ff56f31c665494386d29a226bdf2118f293 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 26 May 2023 17:07:26 -0700 Subject: [PATCH 0292/1030] libsysprof-profile: bridge ring data to capture file --- .../sysprof-controlfd-instrument.c | 105 +++++++++++++++++- 1 file changed, 100 insertions(+), 5 deletions(-) diff --git a/src/libsysprof-profile/sysprof-controlfd-instrument.c b/src/libsysprof-profile/sysprof-controlfd-instrument.c index 41f27235..5280bf24 100644 --- a/src/libsysprof-profile/sysprof-controlfd-instrument.c +++ b/src/libsysprof-profile/sysprof-controlfd-instrument.c @@ -36,9 +36,15 @@ #include "sysprof-controlfd-instrument-private.h" #include "sysprof-recording-private.h" +#ifdef G_OS_UNIX +# include "mapped-ring-buffer.h" +# include "mapped-ring-buffer-source-private.h" +#endif + struct _SysprofControlfdInstrument { SysprofInstrument parent_instance; + #ifdef G_OS_UNIX GUnixConnection *connection; char read_buf[10]; @@ -48,6 +54,37 @@ struct _SysprofControlfdInstrument G_DEFINE_FINAL_TYPE (SysprofControlfdInstrument, sysprof_controlfd_instrument, SYSPROF_TYPE_INSTRUMENT) #ifdef G_OS_UNIX +typedef struct _RingData +{ + SysprofCaptureWriter *writer; + GArray *source_ids; + guint id; +} RingData; + +static void +ring_data_free (gpointer data) +{ + RingData *ring_data = data; + + for (guint i = 0; i < ring_data->source_ids->len; i++) + { + guint *id = &g_array_index (ring_data->source_ids, guint, i); + + if (*id == ring_data->id) + { + *id = 0; + g_array_remove_index_fast (ring_data->source_ids, i); + break; + } + } + + ring_data->id = 0; + + g_clear_pointer (&ring_data->writer, sysprof_capture_writer_unref); + g_array_unref (ring_data->source_ids); + g_free (ring_data); +} + static DexFuture * sysprof_controlfd_instrument_prepare (SysprofInstrument *instrument, SysprofRecording *recording) @@ -122,10 +159,36 @@ sysprof_controlfd_recording_free (gpointer data) g_free (state); } +static bool +sysprof_controlfd_instrument_frame_cb (gconstpointer data, + gsize *length, + gpointer user_data) +{ + const SysprofCaptureFrame *fr = data; + RingData *ring_data = user_data; + + g_assert (ring_data != NULL); + g_assert (ring_data->source_ids != NULL); + g_assert (ring_data->writer != NULL); + g_assert (ring_data->id > 0); + + if G_UNLIKELY (*length < sizeof *fr || + *length < fr->len || + fr->type >= SYSPROF_CAPTURE_FRAME_LAST) + return G_SOURCE_REMOVE; + + _sysprof_capture_writer_add_raw (ring_data->writer, fr); + + *length = fr->len; + + return G_SOURCE_CONTINUE; +} + static DexFuture * sysprof_controlfd_instrument_record_fiber (gpointer user_data) { SysprofControlfdRecording *state = user_data; + g_autoptr(GError) error = NULL; g_assert (state != NULL); g_assert (SYSPROF_IS_RECORDING (state->recording)); @@ -135,9 +198,8 @@ sysprof_controlfd_instrument_record_fiber (gpointer user_data) for (;;) { g_autoptr(DexFuture) future = dex_input_stream_read_bytes (state->stream, 10, 0); - //g_autoptr(MappedRinBuffer) ring_buffer = NULL; + g_autoptr(MappedRingBuffer) ring_buffer = NULL; g_autoptr(GBytes) bytes = NULL; - g_autoptr(GError) error = NULL; const guint8 *data; gsize len; @@ -147,18 +209,47 @@ sysprof_controlfd_instrument_record_fiber (gpointer user_data) &error); if (error != NULL) - return dex_future_new_for_error (g_steal_pointer (&error)); + goto handle_error; if (!(bytes = dex_await_boxed (dex_ref (future), &error))) - return dex_future_new_for_error (g_steal_pointer (&error)); + goto handle_error; data = g_bytes_get_data (bytes, &len); if (len != 10 || memcmp (data, "CreatRing\0", 10) != 0) break; + if ((ring_buffer = mapped_ring_buffer_new_reader (0))) + { + int fd = mapped_ring_buffer_get_fd (ring_buffer); + SysprofCaptureWriter *writer = _sysprof_recording_writer (state->recording); + RingData *ring_data; + + ring_data = g_new0 (RingData, 1); + ring_data->writer = sysprof_capture_writer_ref (writer); + ring_data->source_ids = g_array_ref (state->source_ids); + ring_data->id = mapped_ring_buffer_create_source_full (ring_buffer, + sysprof_controlfd_instrument_frame_cb, + ring_data, + (GDestroyNotify)ring_data_free); + + g_array_append_val (state->source_ids, ring_data->id); + + g_unix_connection_send_fd (G_UNIX_CONNECTION (state->stream), fd, NULL, NULL); + } } - return NULL; +handle_error: + while (state->source_ids->len > 0) + { + guint id = g_array_index (state->source_ids, guint, state->source_ids->len-1); + state->source_ids->len--; + g_source_remove (id); + } + + if (error != NULL) + return dex_future_new_for_error (g_steal_pointer (&error)); + + return dex_future_new_for_boolean (TRUE); } static void @@ -177,10 +268,14 @@ sysprof_controlfd_instrument_record (SysprofInstrument *instrument, { SysprofControlfdInstrument *self = (SysprofControlfdInstrument *)instrument; SysprofControlfdRecording *state; + SysprofSpawnable *spawnable; g_assert (SYSPROF_IS_CONTROLFD_INSTRUMENT (self)); g_assert (SYSPROF_IS_RECORDING (recording)); + if (!(spawnable = _sysprof_recording_get_spawnable (recording))) + return dex_future_new_for_boolean (TRUE); + state = g_new0 (SysprofControlfdRecording, 1); state->recording = g_object_ref (recording); state->cancellable = dex_cancellable_new_from_cancellable (cancellable); From 80bc9ac370f2d60492878e9cea1d71abed2e6210 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 26 May 2023 23:04:22 -0700 Subject: [PATCH 0293/1030] libsysprof-profile: fix declaration alignment --- .../sysprof-instrument-private.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/libsysprof-profile/sysprof-instrument-private.h b/src/libsysprof-profile/sysprof-instrument-private.h index 7755a349..b5ceb73c 100644 --- a/src/libsysprof-profile/sysprof-instrument-private.h +++ b/src/libsysprof-profile/sysprof-instrument-private.h @@ -46,12 +46,12 @@ struct _SysprofInstrumentClass GCancellable *cancellable); }; -DexFuture * _sysprof_instruments_acquire_policy (GPtrArray *instruments, - SysprofRecording *recording); -DexFuture *_sysprof_instruments_prepare (GPtrArray *instruments, - SysprofRecording *recording); -DexFuture *_sysprof_instruments_record (GPtrArray *instruments, - SysprofRecording *recording, - GCancellable *cancellable); +DexFuture *_sysprof_instruments_acquire_policy (GPtrArray *instruments, + SysprofRecording *recording); +DexFuture *_sysprof_instruments_prepare (GPtrArray *instruments, + SysprofRecording *recording); +DexFuture *_sysprof_instruments_record (GPtrArray *instruments, + SysprofRecording *recording, + GCancellable *cancellable); G_END_DECLS From 02811d593baad0471b7a14109487f297896bf142 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Sat, 27 May 2023 10:33:45 -0700 Subject: [PATCH 0294/1030] libsysprof-profile: start on linux host info instrument This instrument is meant to gather information about the host that we will need as part of the capture to decode things properly. --- src/libsysprof-profile/meson.build | 6 + .../sysprof-linux-instrument-private.h | 33 ++++ .../sysprof-linux-instrument.c | 145 ++++++++++++++++++ 3 files changed, 184 insertions(+) create mode 100644 src/libsysprof-profile/sysprof-linux-instrument-private.h create mode 100644 src/libsysprof-profile/sysprof-linux-instrument.c diff --git a/src/libsysprof-profile/meson.build b/src/libsysprof-profile/meson.build index 56f02cd6..fb22f182 100644 --- a/src/libsysprof-profile/meson.build +++ b/src/libsysprof-profile/meson.build @@ -20,6 +20,12 @@ libsysprof_profile_public_headers = [ 'sysprof-spawnable.h', ] +if host_machine.system() == 'linux' + libsysprof_profile_private_sources += [ + 'sysprof-linux-instrument.c', + ] +endif + libsysprof_profile_deps = [ dependency('gio-2.0', version: glib_req_version), dependency('gio-unix-2.0', diff --git a/src/libsysprof-profile/sysprof-linux-instrument-private.h b/src/libsysprof-profile/sysprof-linux-instrument-private.h new file mode 100644 index 00000000..ec6c94be --- /dev/null +++ b/src/libsysprof-profile/sysprof-linux-instrument-private.h @@ -0,0 +1,33 @@ +/* sysprof-linux-instrument-private.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-instrument-private.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_LINUX_INSTRUMENT (sysprof_linux_instrument_get_type()) + +G_DECLARE_FINAL_TYPE (SysprofLinuxInstrument, sysprof_linux_instrument, SYSPROF, LINUX_INSTRUMENT, SysprofInstrument) + +SysprofInstrument *_sysprof_linux_instrument_new (void); + +G_END_DECLS diff --git a/src/libsysprof-profile/sysprof-linux-instrument.c b/src/libsysprof-profile/sysprof-linux-instrument.c new file mode 100644 index 00000000..2d1ff707 --- /dev/null +++ b/src/libsysprof-profile/sysprof-linux-instrument.c @@ -0,0 +1,145 @@ +/* sysprof-linux-instrument.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-linux-instrument-private.h" +#include "sysprof-recording-private.h" + +struct _SysprofLinuxInstrument +{ + SysprofInstrument parent_instance; +}; + +enum { + PROP_0, + N_PROPS +}; + +G_DEFINE_FINAL_TYPE (SysprofLinuxInstrument, sysprof_linux_instrument, SYSPROF_TYPE_INSTRUMENT) + +static gboolean +sysprof_linux_instrument_capture_file (SysprofCaptureWriter *writer, + const char *path, + gboolean compress, + GError **error) +{ + g_autoptr(GBytes) bytes = NULL; + g_autoptr(GFile) file = NULL; + const guint8 *data; + gsize len; + + g_assert (writer != NULL); + g_assert (path != NULL); + + file = g_file_new_for_path (path); + + /* TODO: This needs to go through sysprofd instead */ + + if (!(bytes = dex_await_boxed (dex_file_load_contents_bytes (file), error))) + return FALSE; + + /* TODO: Handle compression of files */ + + data = g_bytes_get_data (bytes, &len); + + while (len > 0) + { + gsize to_write = MIN (len, 4096*8); + + if (!sysprof_capture_writer_add_file (writer, + SYSPROF_CAPTURE_CURRENT_TIME, + -1, + -1, + path, + to_write == len, + data, + to_write)) + break; + + len -= to_write; + data += to_write; + } + + return TRUE; +} + +static DexFuture * +sysprof_linux_instrument_prepare_fiber (gpointer user_data) +{ + SysprofRecording *recording = user_data; + SysprofCaptureWriter *writer; + + g_assert (SYSPROF_IS_RECORDING (recording)); + + writer = _sysprof_recording_writer (recording); + + /* Capture information about the system as we see it from the host system. + * We must query this via sysprofd so that our current process (which may + * have a different mount namespace) does not fall into the capture file. + */ + + sysprof_linux_instrument_capture_file (writer, "/proc/cpuinfo", FALSE, NULL); + sysprof_linux_instrument_capture_file (writer, "/proc/mounts", FALSE, NULL); + sysprof_linux_instrument_capture_file (writer, "/proc/kallsyms", TRUE, NULL); + + return dex_future_new_for_boolean (TRUE); +} + +static DexFuture * +sysprof_linux_instrument_prepare (SysprofInstrument *instrument, + SysprofRecording *recording) +{ + g_assert (SYSPROF_IS_INSTRUMENT (instrument)); + g_assert (SYSPROF_IS_RECORDING (recording)); + + return dex_scheduler_spawn (NULL, 0, + sysprof_linux_instrument_prepare_fiber, + g_object_ref (recording), + g_object_unref); +} + +static void +sysprof_linux_instrument_finalize (GObject *object) +{ + G_OBJECT_CLASS (sysprof_linux_instrument_parent_class)->finalize (object); +} + +static void +sysprof_linux_instrument_class_init (SysprofLinuxInstrumentClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + SysprofInstrumentClass *instrument_class = SYSPROF_INSTRUMENT_CLASS (klass); + + object_class->finalize = sysprof_linux_instrument_finalize; + + instrument_class->prepare = sysprof_linux_instrument_prepare; +} + +static void +sysprof_linux_instrument_init (SysprofLinuxInstrument *self) +{ +} + +SysprofInstrument * +_sysprof_linux_instrument_new (void) +{ + return g_object_new (SYSPROF_TYPE_LINUX_INSTRUMENT, NULL); +} From 01091fe81553df67abd5f6a4e2b5af6633a15381 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Sun, 28 May 2023 09:08:35 -0700 Subject: [PATCH 0295/1030] libsysprof-profiler: add linux instrument on Linux --- src/libsysprof-profile/sysprof-profiler.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/libsysprof-profile/sysprof-profiler.c b/src/libsysprof-profile/sysprof-profiler.c index 6f12a118..0a93432d 100644 --- a/src/libsysprof-profile/sysprof-profiler.c +++ b/src/libsysprof-profile/sysprof-profiler.c @@ -24,6 +24,10 @@ #include "sysprof-profiler.h" #include "sysprof-recording-private.h" +#ifdef __linux__ +# include "sysprof-linux-instrument-private.h" +#endif + struct _SysprofProfiler { GObject parent_instance; @@ -84,6 +88,10 @@ sysprof_profiler_init (SysprofProfiler *self) { self->instruments = g_ptr_array_new_with_free_func (g_object_unref); +#ifdef __linux__ + sysprof_profiler_add_instrument (self, _sysprof_linux_instrument_new ()); +#endif + sysprof_profiler_add_instrument (self, _sysprof_controlfd_instrument_new ()); } From 1e8e1adb73fab3e3c24d0185d4cfd1b613943941 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 30 May 2023 12:46:42 -0700 Subject: [PATCH 0296/1030] libsysprof-analyze: automatically handle compressed streams If we want /proc/kallsyms and we discover /proc/kallsyms.gz, then use the /proc/kallsyms.gz instead and transparently decompress it. Also, list the file in sysprof_document_list_files() as /proc/kallsyms instead of /proc/kallsyms.gz as that is really the intention (but mark the compressed bit for decoding the chunks). --- .../sysprof-document-file-private.h | 3 +- .../sysprof-document-file.c | 18 +++++++--- src/libsysprof-analyze/sysprof-document.c | 21 ++++++++--- .../sysprof-kallsyms-symbolizer.c | 35 ++++++++----------- 4 files changed, 48 insertions(+), 29 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-document-file-private.h b/src/libsysprof-analyze/sysprof-document-file-private.h index c904124d..4f61b926 100644 --- a/src/libsysprof-analyze/sysprof-document-file-private.h +++ b/src/libsysprof-analyze/sysprof-document-file-private.h @@ -25,6 +25,7 @@ G_BEGIN_DECLS SysprofDocumentFile *_sysprof_document_file_new (const char *path, - GPtrArray *file_chunks); + GPtrArray *file_chunks, + gboolean compressed); G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-document-file.c b/src/libsysprof-analyze/sysprof-document-file.c index aa380b61..dd7526c8 100644 --- a/src/libsysprof-analyze/sysprof-document-file.c +++ b/src/libsysprof-analyze/sysprof-document-file.c @@ -26,9 +26,10 @@ struct _SysprofDocumentFile { - GObject parent_instance; - char *path; + GObject parent_instance; + char *path; GPtrArray *file_chunks; + guint compressed : 1; }; enum { @@ -104,7 +105,8 @@ sysprof_document_file_init (SysprofDocumentFile *self) SysprofDocumentFile * _sysprof_document_file_new (const char *path, - GPtrArray *file_chunks) + GPtrArray *file_chunks, + gboolean compressed) { SysprofDocumentFile *self; @@ -114,6 +116,7 @@ _sysprof_document_file_new (const char *path, self = g_object_new (SYSPROF_TYPE_DOCUMENT_FILE, NULL); self->path = g_strdup (path); self->file_chunks = file_chunks; + self->compressed = !!compressed; return self; } @@ -169,7 +172,7 @@ sysprof_document_file_dup_bytes (SysprofDocumentFile *self) GInputStream * sysprof_document_file_read (SysprofDocumentFile *self) { - GInputStream *input; + g_autoptr(GInputStream) input = NULL; g_return_val_if_fail (SYSPROF_IS_DOCUMENT_FILE (self), NULL); @@ -193,6 +196,13 @@ sysprof_document_file_read (SysprofDocumentFile *self) g_memory_input_stream_add_bytes (G_MEMORY_INPUT_STREAM (input), bytes); } + if (self->compressed) + { + g_autoptr(GZlibDecompressor) zlib = g_zlib_decompressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP); + + return g_converter_input_stream_new (input, G_CONVERTER (zlib)); + } + return g_steal_pointer (&input); } diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index 27f42a96..bdefa231 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -895,14 +895,19 @@ SysprofDocumentFile * sysprof_document_lookup_file (SysprofDocument *self, const char *path) { + g_autofree char *gz_path = NULL; gpointer key, value; g_return_val_if_fail (SYSPROF_IS_DOCUMENT (self), NULL); g_return_val_if_fail (path != NULL, NULL); - if (g_hash_table_lookup_extended (self->files_first_position, path, &key, &value)) + gz_path = g_strdup_printf ("%s.gz", path); + + if (g_hash_table_lookup_extended (self->files_first_position, path, &key, &value) || + g_hash_table_lookup_extended (self->files_first_position, gz_path, &key, &value)) { g_autoptr(GPtrArray) file_chunks = g_ptr_array_new_with_free_func (g_object_unref); + const char *real_path = key; EggBitsetIter iter; guint target = GPOINTER_TO_SIZE (value); guint i; @@ -913,7 +918,7 @@ sysprof_document_lookup_file (SysprofDocument *self, { g_autoptr(SysprofDocumentFileChunk) file_chunk = sysprof_document_get_item ((GListModel *)self, i); - if (g_strcmp0 (path, sysprof_document_file_chunk_get_path (file_chunk)) == 0) + if (g_strcmp0 (real_path, sysprof_document_file_chunk_get_path (file_chunk)) == 0) { gboolean is_last = sysprof_document_file_chunk_get_is_last (file_chunk); @@ -926,7 +931,9 @@ sysprof_document_lookup_file (SysprofDocument *self, while (egg_bitset_iter_next (&iter, &i)); } - return _sysprof_document_file_new (path, g_steal_pointer (&file_chunks)); + return _sysprof_document_file_new (path, + g_steal_pointer (&file_chunks), + g_strcmp0 (real_path, gz_path) == 0); } return NULL; @@ -957,6 +964,7 @@ sysprof_document_list_files (SysprofDocument *self) { g_autoptr(SysprofDocumentFile) file = NULL; g_autoptr(GPtrArray) file_chunks = g_ptr_array_new_with_free_func (g_object_unref); + g_autofree char *no_gz_path = NULL; const char *path = key; guint target = GPOINTER_TO_SIZE (value); guint i; @@ -980,7 +988,12 @@ sysprof_document_list_files (SysprofDocument *self) while (egg_bitset_iter_next (&iter, &i)); } - file = _sysprof_document_file_new (path, g_steal_pointer (&file_chunks)); + if (g_str_has_suffix (path, ".gz")) + path = no_gz_path = g_strndup (path, strlen (path) - 3); + + file = _sysprof_document_file_new (path, + g_steal_pointer (&file_chunks), + no_gz_path != NULL); g_list_store_append (model, file); } diff --git a/src/libsysprof-analyze/sysprof-kallsyms-symbolizer.c b/src/libsysprof-analyze/sysprof-kallsyms-symbolizer.c index 2ba237cc..b87c42aa 100644 --- a/src/libsysprof-analyze/sysprof-kallsyms-symbolizer.c +++ b/src/libsysprof-analyze/sysprof-kallsyms-symbolizer.c @@ -191,10 +191,9 @@ sysprof_kallsyms_symbolizer_prepare_async (SysprofSymbolizer *symbolizer, { SysprofKallsymsSymbolizer *self = (SysprofKallsymsSymbolizer *)symbolizer; g_autoptr(SysprofDocumentFile) file = NULL; - g_autoptr(GZlibDecompressor) decompressor = NULL; - g_autoptr(GInputStream) input_gz = NULL; g_autoptr(GInputStream) input = NULL; g_autoptr(GTask) task = NULL; + GInputStream *base_stream; g_assert (SYSPROF_IS_KALLSYMS_SYMBOLIZER (self)); g_assert (SYSPROF_IS_DOCUMENT (document)); @@ -203,9 +202,12 @@ sysprof_kallsyms_symbolizer_prepare_async (SysprofSymbolizer *symbolizer, task = g_task_new (self, cancellable, callback, user_data); g_task_set_source_tag (task, sysprof_kallsyms_symbolizer_prepare_async); - if (self->stream == NULL) + base_stream = self->stream; + + if (base_stream == NULL) { - if (!(file = sysprof_document_lookup_file (document, "/proc/kallsyms.gz"))) + + if (!(file = sysprof_document_lookup_file (document, "/proc/kallsyms"))) { g_task_return_new_error (task, G_IO_ERROR, @@ -214,20 +216,15 @@ sysprof_kallsyms_symbolizer_prepare_async (SysprofSymbolizer *symbolizer, return; } - decompressor = g_zlib_decompressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP); - input_gz = sysprof_document_file_read (file); - input = g_converter_input_stream_new (input_gz, G_CONVERTER (decompressor)); + base_stream = input = sysprof_document_file_read (file); + } - g_task_set_task_data (task, - g_data_input_stream_new (input), - g_object_unref); - } - else - { - g_task_set_task_data (task, - g_steal_pointer (&self->stream), - g_object_unref); - } + g_assert (base_stream != NULL); + g_assert (G_IS_INPUT_STREAM (base_stream)); + + g_task_set_task_data (task, + g_data_input_stream_new (base_stream), + g_object_unref); g_task_run_in_thread (task, sysprof_kallsyms_symbolizer_prepare_worker); } @@ -364,9 +361,7 @@ sysprof_kallsyms_symbolizer_new_for_symbols (GInputStream *symbols) g_return_val_if_fail (G_IS_INPUT_STREAM (symbols), NULL); self = g_object_new (SYSPROF_TYPE_KALLSYMS_SYMBOLIZER, NULL); - self->stream = G_INPUT_STREAM (g_data_input_stream_new (symbols)); - - g_object_unref (symbols); + self->stream = symbols; return SYSPROF_SYMBOLIZER (self); } From 957cec9843b96c7c484195b8955c78e58cf1b685 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 30 May 2023 13:19:57 -0700 Subject: [PATCH 0297/1030] libsysprof-analyze: decompress if necessary in dup_bytes() If we are trying to get the file bytes and they are compressed, we may need to transparently decompress those bytes. That way if the API requested "/proc/cpuinfo" but really got "/proc/cpuinfo.gz" it still gets the same bytes to consume. --- src/libsysprof-analyze/sysprof-document-file.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-document-file.c b/src/libsysprof-analyze/sysprof-document-file.c index dd7526c8..2aa39b62 100644 --- a/src/libsysprof-analyze/sysprof-document-file.c +++ b/src/libsysprof-analyze/sysprof-document-file.c @@ -157,6 +157,23 @@ sysprof_document_file_dup_bytes (SysprofDocumentFile *self) len = ar->len; + if (self->compressed) + { + guint8 *data = (guint8 *)g_array_free (ar, FALSE); + g_autoptr(GInputStream) input = g_memory_input_stream_new_from_data (data, len, g_free); + g_autoptr(GOutputStream) memory_output = g_memory_output_stream_new_resizable (); + g_autoptr(GZlibDecompressor) zlib = g_zlib_decompressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP); + g_autoptr(GOutputStream) zlib_output = g_converter_output_stream_new (memory_output, G_CONVERTER (zlib)); + + g_output_stream_splice (zlib_output, + input, + (G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | + G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET), + NULL, NULL); + + return g_memory_output_stream_steal_as_bytes (G_MEMORY_OUTPUT_STREAM (memory_output)); + } + return g_bytes_new_take (g_array_free (ar, FALSE), len); } From 14f71c5eee7d7288734825822f8d1f85f4230f16 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 30 May 2023 14:43:44 -0700 Subject: [PATCH 0298/1030] libsysprof-profile: add API to append a file to recording --- .../sysprof-recording-private.h | 5 + src/libsysprof-profile/sysprof-recording.c | 137 ++++++++++++++++++ 2 files changed, 142 insertions(+) diff --git a/src/libsysprof-profile/sysprof-recording-private.h b/src/libsysprof-profile/sysprof-recording-private.h index 38f0354d..ecc2eaa7 100644 --- a/src/libsysprof-profile/sysprof-recording-private.h +++ b/src/libsysprof-profile/sysprof-recording-private.h @@ -20,6 +20,8 @@ #pragma once +#include + #include "sysprof-instrument.h" #include "sysprof-recording.h" #include "sysprof-spawnable.h" @@ -32,5 +34,8 @@ SysprofRecording *_sysprof_recording_new (SysprofCaptureWriter *w void _sysprof_recording_start (SysprofRecording *self); SysprofCaptureWriter *_sysprof_recording_writer (SysprofRecording *self); SysprofSpawnable *_sysprof_recording_get_spawnable (SysprofRecording *self); +DexFuture *_sysprof_recording_add_file (SysprofRecording *self, + const char *path, + gboolean compress); G_END_DECLS diff --git a/src/libsysprof-profile/sysprof-recording.c b/src/libsysprof-profile/sysprof-recording.c index 1dc4885d..82545cfe 100644 --- a/src/libsysprof-profile/sysprof-recording.c +++ b/src/libsysprof-profile/sysprof-recording.c @@ -296,3 +296,140 @@ _sysprof_recording_writer (SysprofRecording *self) return self->writer; } + +typedef struct _AddFile +{ + SysprofCaptureWriter *writer; + char *path; + guint compress : 1; +} AddFile; + +static void +add_file_free (AddFile *add_file) +{ + g_clear_pointer (&add_file->writer, sysprof_capture_writer_unref); + g_clear_pointer (&add_file->path, g_free); + g_free (add_file); +} + +static DexFuture * +sysprof_recording_add_file_fiber (gpointer user_data) +{ + AddFile *add_file = user_data; + g_autoptr(GInputStream) input = NULL; + g_autoptr(GOutputStream) memory_stream = NULL; + g_autoptr(GOutputStream) zlib_stream = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GBytes) bytes = NULL; + g_autoptr(GFile) proc = NULL; + g_autoptr(GFile) file = NULL; + g_autofree char *dest_path = NULL; + const guint8 *data = NULL; + GOutputStream *output; + gsize len; + + g_assert (add_file != NULL); + g_assert (add_file->path != NULL); + g_assert (add_file->writer != NULL); + + /* If we are compressing the file, store it as ".gz" in the capture + * so that the reader knows to decompress it automatically. + */ + dest_path = add_file->compress ? g_strdup_printf ("%s.gz", add_file->path) + : g_strdup (add_file->path); + + file = g_file_new_for_path (add_file->path); + proc = g_file_new_for_path ("/proc"); + + /* If the file has a prefix of `/proc/` then we need to request the + * file from sysprofd as our user is not guaranteed to be able to + * read the file. Even if we can open the file, we may get data that + * has been redacted (such as /proc/kallsyms). + * + * With `/proc/kallsyms` it's even worse in that if we get an FD back + * from sysprofd and read it from our process, it *too* will be redacted. + * That leaves us with the only option of letting sysprofd read the file + * and transfer the contents to our process over D-Bus. + * + * We use g_file_has_prefix() for this as it will canonicalize the paths + * of #GFile rather than us having to be careful here. + */ + //if (g_file_has_prefix (file, proc)) + //{ + //} + //else + { + if (!(input = dex_await_object (dex_file_read (file, 0), &error))) + return dex_future_new_for_error (g_steal_pointer (&error)); + } + + g_assert (input != NULL); + g_assert (G_IS_INPUT_STREAM (input)); + + output = memory_stream = g_memory_output_stream_new_resizable (); + + if (add_file->compress) + { + g_autoptr(GZlibCompressor) compressor = g_zlib_compressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP, 6); + output = zlib_stream = g_converter_output_stream_new (memory_stream, G_CONVERTER (compressor)); + } + + g_assert (output != NULL); + g_assert (G_IS_OUTPUT_STREAM (output)); + g_assert (G_IS_MEMORY_OUTPUT_STREAM (output) || G_IS_CONVERTER_OUTPUT_STREAM (output)); + g_assert (G_IS_MEMORY_OUTPUT_STREAM (memory_stream)); + g_assert (!zlib_stream || G_IS_CONVERTER_OUTPUT_STREAM (zlib_stream)); + + if (!dex_await (dex_output_stream_splice (output, + input, + (G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | + G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET), + 0), + &error)) + return dex_future_new_for_error (g_steal_pointer (&error)); + + bytes = g_memory_output_stream_steal_as_bytes (G_MEMORY_OUTPUT_STREAM (memory_stream)); + data = g_bytes_get_data (bytes, &len); + + while (len > 0) + { + gsize to_write = MIN (len, 4096*8); + + if (!sysprof_capture_writer_add_file (add_file->writer, + SYSPROF_CAPTURE_CURRENT_TIME, + -1, + -1, + dest_path, + to_write == len, + data, + to_write)) + break; + + len -= to_write; + data += to_write; + } + + return dex_future_new_for_boolean (TRUE); +} + +DexFuture * +_sysprof_recording_add_file (SysprofRecording *self, + const char *path, + gboolean compress) +{ + g_autoptr(GFile) file = NULL; + AddFile *add_file; + + g_return_val_if_fail (SYSPROF_IS_RECORDING (self), NULL); + g_return_val_if_fail (path != NULL, NULL); + + add_file = g_new0 (AddFile, 1); + add_file->writer = sysprof_capture_writer_ref (self->writer); + add_file->path = g_strdup (path); + add_file->compress = !!compress; + + return dex_scheduler_spawn (NULL, 0, + sysprof_recording_add_file_fiber, + g_steal_pointer (&add_file), + (GDestroyNotify)add_file_free); +} From 4fe9339113c5c459831e721733eabe7c20c9545e Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 30 May 2023 14:44:01 -0700 Subject: [PATCH 0299/1030] libsysprof-profile: capture files using recording API --- .../sysprof-linux-instrument.c | 63 ++----------------- 1 file changed, 4 insertions(+), 59 deletions(-) diff --git a/src/libsysprof-profile/sysprof-linux-instrument.c b/src/libsysprof-profile/sysprof-linux-instrument.c index 2d1ff707..071c52ab 100644 --- a/src/libsysprof-profile/sysprof-linux-instrument.c +++ b/src/libsysprof-profile/sysprof-linux-instrument.c @@ -35,72 +35,17 @@ enum { G_DEFINE_FINAL_TYPE (SysprofLinuxInstrument, sysprof_linux_instrument, SYSPROF_TYPE_INSTRUMENT) -static gboolean -sysprof_linux_instrument_capture_file (SysprofCaptureWriter *writer, - const char *path, - gboolean compress, - GError **error) -{ - g_autoptr(GBytes) bytes = NULL; - g_autoptr(GFile) file = NULL; - const guint8 *data; - gsize len; - - g_assert (writer != NULL); - g_assert (path != NULL); - - file = g_file_new_for_path (path); - - /* TODO: This needs to go through sysprofd instead */ - - if (!(bytes = dex_await_boxed (dex_file_load_contents_bytes (file), error))) - return FALSE; - - /* TODO: Handle compression of files */ - - data = g_bytes_get_data (bytes, &len); - - while (len > 0) - { - gsize to_write = MIN (len, 4096*8); - - if (!sysprof_capture_writer_add_file (writer, - SYSPROF_CAPTURE_CURRENT_TIME, - -1, - -1, - path, - to_write == len, - data, - to_write)) - break; - - len -= to_write; - data += to_write; - } - - return TRUE; -} - static DexFuture * sysprof_linux_instrument_prepare_fiber (gpointer user_data) { SysprofRecording *recording = user_data; - SysprofCaptureWriter *writer; g_assert (SYSPROF_IS_RECORDING (recording)); - writer = _sysprof_recording_writer (recording); - - /* Capture information about the system as we see it from the host system. - * We must query this via sysprofd so that our current process (which may - * have a different mount namespace) does not fall into the capture file. - */ - - sysprof_linux_instrument_capture_file (writer, "/proc/cpuinfo", FALSE, NULL); - sysprof_linux_instrument_capture_file (writer, "/proc/mounts", FALSE, NULL); - sysprof_linux_instrument_capture_file (writer, "/proc/kallsyms", TRUE, NULL); - - return dex_future_new_for_boolean (TRUE); + return dex_future_all (_sysprof_recording_add_file (recording, "/proc/kallsyms", TRUE), + _sysprof_recording_add_file (recording, "/proc/cpuinfo", TRUE), + _sysprof_recording_add_file (recording, "/proc/mounts", TRUE), + NULL); } static DexFuture * From f9be133913a22d5d7be5df3f198b6abcf98ae99b Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 30 May 2023 15:53:27 -0700 Subject: [PATCH 0300/1030] libsysprof-profile: implement loading files via sysprofd If we need access to a privileged file in /proc or /sys we need to get that through sysprofd. This implements basic checking of paths to see if we need to get a /proc file from there. We can't use the GetProfFD variant because that may still cause errors when reading back due to how selinux and other LSMs may restrict read() to get kallsyms. This requires recent API additions in libdex. --- src/libsysprof-profile/sysprof-recording.c | 29 +++++++++++++++++++--- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/src/libsysprof-profile/sysprof-recording.c b/src/libsysprof-profile/sysprof-recording.c index 82545cfe..53a245e3 100644 --- a/src/libsysprof-profile/sysprof-recording.c +++ b/src/libsysprof-profile/sysprof-recording.c @@ -354,10 +354,31 @@ sysprof_recording_add_file_fiber (gpointer user_data) * We use g_file_has_prefix() for this as it will canonicalize the paths * of #GFile rather than us having to be careful here. */ - //if (g_file_has_prefix (file, proc)) - //{ - //} - //else + if (g_file_has_prefix (file, proc)) + { + g_autoptr(GDBusConnection) connection = NULL; + g_autoptr(GVariant) reply = NULL; + g_autoptr(GBytes) input_bytes = NULL; + + if (!(connection = dex_await_object (dex_bus_get (G_BUS_TYPE_SYSTEM), &error))) + return dex_future_new_for_error (g_steal_pointer (&error)); + + if (!(reply = dex_await_variant (dex_dbus_connection_call (connection, + "org.gnome.Sysprof3", + "/org/gnome/Sysprof3", + "org.gnome.Sysprof3.Service", + "GetProcFile", + g_variant_new ("(^ay)", g_file_get_path (file)), + G_VARIANT_TYPE ("(ay)"), + G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION, + G_MAXINT), + &error))) + return dex_future_new_for_error (g_steal_pointer (&error)); + + input_bytes = g_variant_get_data_as_bytes (reply); + input = g_memory_input_stream_new_from_bytes (input_bytes); + } + else { if (!(input = dex_await_object (dex_file_read (file, 0), &error))) return dex_future_new_for_error (g_steal_pointer (&error)); From 1851f39b8aaff294cda076d5599c3e90f8ee9080 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 30 May 2023 16:05:01 -0700 Subject: [PATCH 0301/1030] libsysprof-profile: use dex_bus_get() This allows us to remove the synchronous call here from the fiber calling into this API. --- src/libsysprof-profile/sysprof-instrument.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libsysprof-profile/sysprof-instrument.c b/src/libsysprof-profile/sysprof-instrument.c index d2c1f27f..72ff5f09 100644 --- a/src/libsysprof-profile/sysprof-instrument.c +++ b/src/libsysprof-profile/sysprof-instrument.c @@ -118,7 +118,7 @@ _sysprof_instruments_acquire_policy (GPtrArray *instruments, /* Ensure we have access to the System D-Bus so that we can get * access to sysprofd for system information. */ - if (!(connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error))) + if (!(connection = dex_await_object (dex_bus_get (G_BUS_TYPE_SYSTEM), &error))) return dex_future_new_for_error (g_steal_pointer (&error)); /* First ensure that all our required policy have been acquired on From 1764a088eefd2eafa35ed6d5bbe59a3262656583 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 30 May 2023 16:10:19 -0700 Subject: [PATCH 0302/1030] libsysprof-profile: fix iteration of instruments --- src/libsysprof-profile/sysprof-instrument.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libsysprof-profile/sysprof-instrument.c b/src/libsysprof-profile/sysprof-instrument.c index 72ff5f09..242f8bf3 100644 --- a/src/libsysprof-profile/sysprof-instrument.c +++ b/src/libsysprof-profile/sysprof-instrument.c @@ -77,12 +77,12 @@ _sysprof_instruments_list_required_policy (GPtrArray *instruments) all_policy = g_ptr_array_new_null_terminated (0, g_free, TRUE); - for (guint i = 0; i > instruments->len; i++) + for (guint i = 0; i < instruments->len; i++) { SysprofInstrument *instrument = g_ptr_array_index (instruments, i); g_auto(GStrv) policy = SYSPROF_INSTRUMENT_GET_CLASS (instrument)->list_required_policy (instrument); - if (policy == NULL) + if (policy == NULL || policy[0] == NULL) continue; for (guint j = 0; policy[j]; j++) From f91e13e0555e95ea28634561d6f80ccb7a72f829 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 30 May 2023 16:11:38 -0700 Subject: [PATCH 0303/1030] libsysprof-profile: require org.gnome.sysprof3.profile policy This will be needed to read the given proc files that should be captured in the recording. --- src/libsysprof-profile/sysprof-linux-instrument.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/libsysprof-profile/sysprof-linux-instrument.c b/src/libsysprof-profile/sysprof-linux-instrument.c index 071c52ab..44eaf9d2 100644 --- a/src/libsysprof-profile/sysprof-linux-instrument.c +++ b/src/libsysprof-profile/sysprof-linux-instrument.c @@ -35,6 +35,14 @@ enum { G_DEFINE_FINAL_TYPE (SysprofLinuxInstrument, sysprof_linux_instrument, SYSPROF_TYPE_INSTRUMENT) +static char ** +sysprof_linux_instrument_list_required_policy (SysprofInstrument *instrument) +{ + static const char *policy[] = {"org.gnome.sysprof3.profile", NULL}; + + return g_strdupv ((char **)policy); +} + static DexFuture * sysprof_linux_instrument_prepare_fiber (gpointer user_data) { @@ -75,6 +83,7 @@ sysprof_linux_instrument_class_init (SysprofLinuxInstrumentClass *klass) object_class->finalize = sysprof_linux_instrument_finalize; + instrument_class->list_required_policy = sysprof_linux_instrument_list_required_policy; instrument_class->prepare = sysprof_linux_instrument_prepare; } From df7da9bb411d67e71c210151a8716d2c6f04045b Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 30 May 2023 17:00:00 -0700 Subject: [PATCH 0304/1030] libsysprof-profile: remove frame overhead from max data length Just so that we can potentially fit multiple of these per buffering in the capture writer. --- src/libsysprof-profile/sysprof-recording.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libsysprof-profile/sysprof-recording.c b/src/libsysprof-profile/sysprof-recording.c index 53a245e3..d455fc34 100644 --- a/src/libsysprof-profile/sysprof-recording.c +++ b/src/libsysprof-profile/sysprof-recording.c @@ -414,7 +414,7 @@ sysprof_recording_add_file_fiber (gpointer user_data) while (len > 0) { - gsize to_write = MIN (len, 4096*8); + gsize to_write = MIN (len, ((4096*8)-sizeof (SysprofCaptureFileChunk))); if (!sysprof_capture_writer_add_file (add_file->writer, SYSPROF_CAPTURE_CURRENT_TIME, From 81317bc1caa773b1d633196841cabf19a017797c Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 30 May 2023 17:00:53 -0700 Subject: [PATCH 0305/1030] libsysprof-profile: add recording helper to capture file data We might have the file data from the peer and can specify the data in the capture writer directly. It may be useful to allow compressing these too, but we can deal with that at a later time. --- .../sysprof-recording-private.h | 4 +++ src/libsysprof-profile/sysprof-recording.c | 32 +++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/src/libsysprof-profile/sysprof-recording-private.h b/src/libsysprof-profile/sysprof-recording-private.h index ecc2eaa7..9e553ec0 100644 --- a/src/libsysprof-profile/sysprof-recording-private.h +++ b/src/libsysprof-profile/sysprof-recording-private.h @@ -37,5 +37,9 @@ SysprofSpawnable *_sysprof_recording_get_spawnable (SysprofRecording *s DexFuture *_sysprof_recording_add_file (SysprofRecording *self, const char *path, gboolean compress); +void _sysprof_recording_add_file_data (SysprofRecording *self, + const char *path, + const char *contents, + gssize length); G_END_DECLS diff --git a/src/libsysprof-profile/sysprof-recording.c b/src/libsysprof-profile/sysprof-recording.c index d455fc34..a2f2d590 100644 --- a/src/libsysprof-profile/sysprof-recording.c +++ b/src/libsysprof-profile/sysprof-recording.c @@ -454,3 +454,35 @@ _sysprof_recording_add_file (SysprofRecording *self, g_steal_pointer (&add_file), (GDestroyNotify)add_file_free); } + +void +_sysprof_recording_add_file_data (SysprofRecording *self, + const char *path, + const char *contents, + gssize length) +{ + g_return_if_fail (SYSPROF_IS_RECORDING (self)); + g_return_if_fail (path != NULL); + g_return_if_fail (contents != NULL); + + if (length < 0) + length = strlen (contents); + + while (length > 0) + { + gsize to_write = MIN (length, ((4096*8)-sizeof (SysprofCaptureFileChunk))); + + if (!sysprof_capture_writer_add_file (self->writer, + SYSPROF_CAPTURE_CURRENT_TIME, + -1, + -1, + path, + to_write == length, + (const guint8 *)contents, + to_write)) + break; + + length -= to_write; + contents += to_write; + } +} From 5c825c1fb5709e237688a0825ce31e544e658404 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 30 May 2023 17:06:28 -0700 Subject: [PATCH 0306/1030] libsysprof-profile: add some basic process information This needs to get into the capture, and we still need to respond to the discovery of new processes as well (which we lack API for). --- .../sysprof-linux-instrument.c | 112 +++++++++++++++++- 1 file changed, 108 insertions(+), 4 deletions(-) diff --git a/src/libsysprof-profile/sysprof-linux-instrument.c b/src/libsysprof-profile/sysprof-linux-instrument.c index 44eaf9d2..b0300648 100644 --- a/src/libsysprof-profile/sysprof-linux-instrument.c +++ b/src/libsysprof-profile/sysprof-linux-instrument.c @@ -43,17 +43,121 @@ sysprof_linux_instrument_list_required_policy (SysprofInstrument *instrument) return g_strdupv ((char **)policy); } +static void +add_process_info (SysprofRecording *recording, + GVariant *process_info_reply) +{ + g_autoptr(GPtrArray) futures = NULL; + g_autoptr(GVariant) process_info = NULL; + SysprofCaptureWriter *writer; + GVariantIter iter; + GVariant *pidinfo; + + g_assert (process_info_reply != NULL); + g_assert (g_variant_is_of_type (process_info_reply, G_VARIANT_TYPE ("(aa{sv})"))); + + writer = _sysprof_recording_writer (recording); + + /* Loop through all the PIDs the server notified us about */ + process_info = g_variant_get_child_value (process_info_reply, 0); + g_variant_iter_init (&iter, process_info); + while (g_variant_iter_loop (&iter, "@a{sv}", &pidinfo)) + { + g_autofree char *mount_path = NULL; + GVariantDict dict; + const char *cmdline; + const char *comm; + const char *mountinfo; + const char *maps; + const char *cgroup; + gint32 pid; + + g_variant_dict_init (&dict, pidinfo); + + if (!g_variant_dict_lookup (&dict, "pid", "i", &pid)) + goto skip; + + if (!g_variant_dict_lookup (&dict, "cmdline", "&s", &cmdline)) + cmdline = ""; + + if (!g_variant_dict_lookup (&dict, "comm", "&s", &comm)) + comm = ""; + + if (!g_variant_dict_lookup (&dict, "mountinfo", "&s", &mountinfo)) + mountinfo = ""; + + if (!g_variant_dict_lookup (&dict, "maps", "&s", &maps)) + maps = ""; + + if (!g_variant_dict_lookup (&dict, "cgroup", "&s", &cgroup)) + cgroup = ""; + + /* Notify the capture that a process was spawned */ + sysprof_capture_writer_add_process (writer, + SYSPROF_CAPTURE_CURRENT_TIME, + -1, + pid, + *cmdline ? cmdline : comm); + + /* Give the capture access to the mountinfo of that process to aid + * in resolving symbols later on. + */ + mount_path = g_strdup_printf ("/proc/%u/mountinfo", pid); + _sysprof_recording_add_file_data (recording, mount_path, mountinfo, -1); + + // TODO + //sysprof_proc_source_populate_maps (self, pid, maps, ignore_inode); + //sysprof_proc_source_populate_overlays (self, pid, cgroup); + + skip: + g_variant_dict_clear (&dict); + } +} + static DexFuture * sysprof_linux_instrument_prepare_fiber (gpointer user_data) { SysprofRecording *recording = user_data; + g_autoptr(GDBusConnection) bus = NULL; + g_autoptr(GVariant) process_info_reply = NULL; + g_autoptr(GVariant) process_info = NULL; + g_autoptr(GError) error = NULL; g_assert (SYSPROF_IS_RECORDING (recording)); - return dex_future_all (_sysprof_recording_add_file (recording, "/proc/kallsyms", TRUE), - _sysprof_recording_add_file (recording, "/proc/cpuinfo", TRUE), - _sysprof_recording_add_file (recording, "/proc/mounts", TRUE), - NULL); + /* First get some basic information about the system into the capture. We can + * get the contents for all of these concurrently. + */ + if (!dex_await (dex_future_all (_sysprof_recording_add_file (recording, "/proc/kallsyms", TRUE), + _sysprof_recording_add_file (recording, "/proc/cpuinfo", TRUE), + _sysprof_recording_add_file (recording, "/proc/mounts", TRUE), + NULL), + &error)) + return dex_future_new_for_error (g_steal_pointer (&error)); + + /* We need access to the bus to call various sysprofd API directly */ + if (!(bus = dex_await_object (dex_bus_get (G_BUS_TYPE_SYSTEM), &error))) + return dex_future_new_for_error (g_steal_pointer (&error)); + + /* We also want to get a bunch of info on user processes so that we can add + * records about them to the recording. + */ + if (!(process_info_reply = dex_await_variant (dex_dbus_connection_call (bus, + "org.gnome.Sysprof3", + "/org/gnome/Sysprof3", + "org.gnome.Sysprof3.Service", + "GetProcessInfo", + g_variant_new ("(s)", "pid,maps,mountinfo,cmdline,comm,cgroup"), + G_VARIANT_TYPE ("(aa{sv})"), + G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION, + G_MAXINT), + &error))) + return dex_future_new_for_error (g_steal_pointer (&error)); + + /* Add process records for each of the processes discovered */ + add_process_info (recording, process_info_reply); + + return dex_future_new_for_boolean (TRUE); } static DexFuture * From ee3a78433fb868a365c5db7b7bdd75248bc4fb2f Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 30 May 2023 17:12:19 -0700 Subject: [PATCH 0307/1030] libsysprof-profile: add processes during recording We want to reduce the chances we miss things between prepare and record virtual methods, so delay the adding of processes until the recording is started. We may get duplicate records, but we can deal with that when analyzing the capture. --- .../sysprof-linux-instrument.c | 49 ++++++++++++++----- 1 file changed, 37 insertions(+), 12 deletions(-) diff --git a/src/libsysprof-profile/sysprof-linux-instrument.c b/src/libsysprof-profile/sysprof-linux-instrument.c index b0300648..c970a092 100644 --- a/src/libsysprof-profile/sysprof-linux-instrument.c +++ b/src/libsysprof-profile/sysprof-linux-instrument.c @@ -45,21 +45,19 @@ sysprof_linux_instrument_list_required_policy (SysprofInstrument *instrument) static void add_process_info (SysprofRecording *recording, - GVariant *process_info_reply) + GVariant *process_info) { g_autoptr(GPtrArray) futures = NULL; - g_autoptr(GVariant) process_info = NULL; SysprofCaptureWriter *writer; GVariantIter iter; GVariant *pidinfo; - g_assert (process_info_reply != NULL); - g_assert (g_variant_is_of_type (process_info_reply, G_VARIANT_TYPE ("(aa{sv})"))); + g_assert (process_info != NULL); + g_assert (g_variant_is_of_type (process_info, G_VARIANT_TYPE ("aa{sv}"))); writer = _sysprof_recording_writer (recording); /* Loop through all the PIDs the server notified us about */ - process_info = g_variant_get_child_value (process_info_reply, 0); g_variant_iter_init (&iter, process_info); while (g_variant_iter_loop (&iter, "@a{sv}", &pidinfo)) { @@ -118,9 +116,6 @@ static DexFuture * sysprof_linux_instrument_prepare_fiber (gpointer user_data) { SysprofRecording *recording = user_data; - g_autoptr(GDBusConnection) bus = NULL; - g_autoptr(GVariant) process_info_reply = NULL; - g_autoptr(GVariant) process_info = NULL; g_autoptr(GError) error = NULL; g_assert (SYSPROF_IS_RECORDING (recording)); @@ -135,6 +130,33 @@ sysprof_linux_instrument_prepare_fiber (gpointer user_data) &error)) return dex_future_new_for_error (g_steal_pointer (&error)); + return dex_future_new_for_boolean (TRUE); +} + +static DexFuture * +sysprof_linux_instrument_prepare (SysprofInstrument *instrument, + SysprofRecording *recording) +{ + g_assert (SYSPROF_IS_INSTRUMENT (instrument)); + g_assert (SYSPROF_IS_RECORDING (recording)); + + return dex_scheduler_spawn (NULL, 0, + sysprof_linux_instrument_prepare_fiber, + g_object_ref (recording), + g_object_unref); +} + +static DexFuture * +sysprof_linux_instrument_record_fiber (gpointer user_data) +{ + SysprofRecording *recording = user_data; + g_autoptr(GDBusConnection) bus = NULL; + g_autoptr(GVariant) process_info_reply = NULL; + g_autoptr(GVariant) process_info = NULL; + g_autoptr(GError) error = NULL; + + g_assert (SYSPROF_IS_RECORDING (recording)); + /* We need access to the bus to call various sysprofd API directly */ if (!(bus = dex_await_object (dex_bus_get (G_BUS_TYPE_SYSTEM), &error))) return dex_future_new_for_error (g_steal_pointer (&error)); @@ -155,20 +177,22 @@ sysprof_linux_instrument_prepare_fiber (gpointer user_data) return dex_future_new_for_error (g_steal_pointer (&error)); /* Add process records for each of the processes discovered */ - add_process_info (recording, process_info_reply); + process_info = g_variant_get_child_value (process_info_reply, 0); + add_process_info (recording, process_info); return dex_future_new_for_boolean (TRUE); } static DexFuture * -sysprof_linux_instrument_prepare (SysprofInstrument *instrument, - SysprofRecording *recording) +sysprof_linux_instrument_record (SysprofInstrument *instrument, + SysprofRecording *recording, + GCancellable *cancellable) { g_assert (SYSPROF_IS_INSTRUMENT (instrument)); g_assert (SYSPROF_IS_RECORDING (recording)); return dex_scheduler_spawn (NULL, 0, - sysprof_linux_instrument_prepare_fiber, + sysprof_linux_instrument_record_fiber, g_object_ref (recording), g_object_unref); } @@ -189,6 +213,7 @@ sysprof_linux_instrument_class_init (SysprofLinuxInstrumentClass *klass) instrument_class->list_required_policy = sysprof_linux_instrument_list_required_policy; instrument_class->prepare = sysprof_linux_instrument_prepare; + instrument_class->record = sysprof_linux_instrument_record; } static void From 2ac4ff692dc6d310018f3348ca40f28f81a65d5d Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 30 May 2023 17:27:33 -0700 Subject: [PATCH 0308/1030] libsysprof-profile: add API to notify of process started This will be used when we discover a process started like when the Perf instrument gets PERF_RECORD_COMM. --- .../sysprof-instrument-private.h | 20 ++++++++----- src/libsysprof-profile/sysprof-instrument.c | 30 +++++++++++++++++++ 2 files changed, 43 insertions(+), 7 deletions(-) diff --git a/src/libsysprof-profile/sysprof-instrument-private.h b/src/libsysprof-profile/sysprof-instrument-private.h index b5ceb73c..f8a4d9df 100644 --- a/src/libsysprof-profile/sysprof-instrument-private.h +++ b/src/libsysprof-profile/sysprof-instrument-private.h @@ -44,14 +44,20 @@ struct _SysprofInstrumentClass DexFuture *(*record) (SysprofInstrument *self, SysprofRecording *recording, GCancellable *cancellable); + DexFuture *(*process_started) (SysprofInstrument *self, + SysprofRecording *recording, + int pid); }; -DexFuture *_sysprof_instruments_acquire_policy (GPtrArray *instruments, - SysprofRecording *recording); -DexFuture *_sysprof_instruments_prepare (GPtrArray *instruments, - SysprofRecording *recording); -DexFuture *_sysprof_instruments_record (GPtrArray *instruments, - SysprofRecording *recording, - GCancellable *cancellable); +DexFuture *_sysprof_instruments_acquire_policy (GPtrArray *instruments, + SysprofRecording *recording); +DexFuture *_sysprof_instruments_prepare (GPtrArray *instruments, + SysprofRecording *recording); +DexFuture *_sysprof_instruments_record (GPtrArray *instruments, + SysprofRecording *recording, + GCancellable *cancellable); +DexFuture *_sysprof_instruments_process_started (GPtrArray *instruments, + SysprofRecording *recording, + int pid); G_END_DECLS diff --git a/src/libsysprof-profile/sysprof-instrument.c b/src/libsysprof-profile/sysprof-instrument.c index 242f8bf3..37eba55d 100644 --- a/src/libsysprof-profile/sysprof-instrument.c +++ b/src/libsysprof-profile/sysprof-instrument.c @@ -174,6 +174,7 @@ _sysprof_instruments_record (GPtrArray *instruments, g_return_val_if_fail (instruments != NULL, NULL); g_return_val_if_fail (SYSPROF_IS_RECORDING (recording), NULL); + g_return_val_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable), NULL); futures = g_ptr_array_new_with_free_func (dex_unref); @@ -190,3 +191,32 @@ _sysprof_instruments_record (GPtrArray *instruments, return dex_future_allv ((DexFuture **)futures->pdata, futures->len); } + +DexFuture * +_sysprof_instruments_process_started (GPtrArray *instruments, + SysprofRecording *recording, + int pid) +{ + g_autoptr(GPtrArray) futures = NULL; + + g_return_val_if_fail (instruments != NULL, NULL); + g_return_val_if_fail (SYSPROF_IS_RECORDING (recording), NULL); + + futures = g_ptr_array_new_with_free_func (dex_unref); + + for (guint i = 0; i < instruments->len; i++) + { + SysprofInstrument *instrument = g_ptr_array_index (instruments, i); + + if (SYSPROF_INSTRUMENT_GET_CLASS (instruments)->process_started == NULL) + continue; + + g_ptr_array_add (futures, + SYSPROF_INSTRUMENT_GET_CLASS (instrument)->process_started (instrument, recording, pid)); + } + + if (futures->len == 0) + return dex_future_new_for_boolean (TRUE); + + return dex_future_allv ((DexFuture **)futures->pdata, futures->len); +} From 8ddcf54c37c095c454d5acbeece3fa4d7465e587 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 30 May 2023 17:28:04 -0700 Subject: [PATCH 0309/1030] libsysprof-profile: add mountinfo when process discovered --- .../sysprof-linux-instrument.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/libsysprof-profile/sysprof-linux-instrument.c b/src/libsysprof-profile/sysprof-linux-instrument.c index c970a092..0a2063d4 100644 --- a/src/libsysprof-profile/sysprof-linux-instrument.c +++ b/src/libsysprof-profile/sysprof-linux-instrument.c @@ -197,6 +197,21 @@ sysprof_linux_instrument_record (SysprofInstrument *instrument, g_object_unref); } +static DexFuture * +sysprof_linux_instrument_process_started (SysprofInstrument *instrument, + SysprofRecording *recording, + int pid) +{ + g_autofree char *mountinfo_path = NULL; + + g_assert (SYSPROF_IS_INSTRUMENT (instrument)); + g_assert (SYSPROF_IS_RECORDING (recording)); + + mountinfo_path = g_strdup_printf ("/proc/%u/mountinfo", pid); + + return _sysprof_recording_add_file (recording, mountinfo_path, TRUE); +} + static void sysprof_linux_instrument_finalize (GObject *object) { @@ -214,6 +229,7 @@ sysprof_linux_instrument_class_init (SysprofLinuxInstrumentClass *klass) instrument_class->list_required_policy = sysprof_linux_instrument_list_required_policy; instrument_class->prepare = sysprof_linux_instrument_prepare; instrument_class->record = sysprof_linux_instrument_record; + instrument_class->process_started = sysprof_linux_instrument_process_started; } static void From 1840c7b526a3680c059b8bcb73362f808f275d8d Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 30 May 2023 17:35:00 -0700 Subject: [PATCH 0310/1030] libsysprof-profile: add mmap records for existing processes --- .../sysprof-linux-instrument.c | 68 ++++++++++++++++++- 1 file changed, 67 insertions(+), 1 deletion(-) diff --git a/src/libsysprof-profile/sysprof-linux-instrument.c b/src/libsysprof-profile/sysprof-linux-instrument.c index 0a2063d4..f43928cf 100644 --- a/src/libsysprof-profile/sysprof-linux-instrument.c +++ b/src/libsysprof-profile/sysprof-linux-instrument.c @@ -20,6 +20,8 @@ #include "config.h" +#include + #include "sysprof-linux-instrument-private.h" #include "sysprof-recording-private.h" @@ -43,6 +45,64 @@ sysprof_linux_instrument_list_required_policy (SysprofInstrument *instrument) return g_strdupv ((char **)policy); } +static void +add_mmaps (SysprofRecording *recording, + GPid pid, + const char *mapsstr, + gboolean ignore_inode) +{ + SysprofCaptureWriter *writer; + g_auto(GStrv) lines = NULL; + + g_assert (SYSPROF_IS_RECORDING (recording)); + g_assert (mapsstr != NULL); + g_assert (pid > 0); + + writer = _sysprof_recording_writer (recording); + lines = g_strsplit (mapsstr, "\n", 0); + + for (guint i = 0; lines[i] != NULL; i++) + { + char file[512]; + gulong start; + gulong end; + gulong offset; + gulong inode; + int r; + gboolean is_vdso; + + r = sscanf (lines[i], + "%lx-%lx %*15s %lx %*x:%*x %lu %511[^\n]", + &start, &end, &offset, &inode, file); + file [sizeof file - 1] = '\0'; + + /* file has a " (deleted)" suffix if it was deleted from disk */ + if (g_str_has_suffix (file, " (deleted)")) + file [strlen (file) - strlen (" (deleted)")] = '\0'; + + if (r != 5) + continue; + + is_vdso = strcmp ("[vdso]", file) == 0; + + if (ignore_inode || is_vdso) + inode = 0; + + if (is_vdso) + offset = 0; + + sysprof_capture_writer_add_map (writer, + SYSPROF_CAPTURE_CURRENT_TIME, + -1, + pid, + start, + end, + offset, + inode, + file); + } +} + static void add_process_info (SysprofRecording *recording, GVariant *process_info) @@ -68,6 +128,7 @@ add_process_info (SysprofRecording *recording, const char *mountinfo; const char *maps; const char *cgroup; + gboolean ignore_inode; gint32 pid; g_variant_dict_init (&dict, pidinfo); @@ -103,8 +164,13 @@ add_process_info (SysprofRecording *recording, mount_path = g_strdup_printf ("/proc/%u/mountinfo", pid); _sysprof_recording_add_file_data (recording, mount_path, mountinfo, -1); + /* Ignore inodes from podman/toolbox because they appear to always be + * wrong. We'll have to rely on CRC/build-id instead. + */ + ignore_inode = strstr (cgroup, "/libpod-") != NULL; + add_mmaps (recording, pid, maps, ignore_inode); + // TODO - //sysprof_proc_source_populate_maps (self, pid, maps, ignore_inode); //sysprof_proc_source_populate_overlays (self, pid, cgroup); skip: From 9f9f953d5321bbed1862e0e36752ba8a75dfe8c4 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 30 May 2023 17:57:41 -0700 Subject: [PATCH 0311/1030] libsysprof-profile: import podman utils This comes from libsysprof, which we hope to remove as part of this move to libsysprof-profile vs libsysprof-analyze. --- src/libsysprof-profile/meson.build | 2 + .../sysprof-podman-private.h | 36 +++ src/libsysprof-profile/sysprof-podman.c | 274 ++++++++++++++++++ 3 files changed, 312 insertions(+) create mode 100644 src/libsysprof-profile/sysprof-podman-private.h create mode 100644 src/libsysprof-profile/sysprof-podman.c diff --git a/src/libsysprof-profile/meson.build b/src/libsysprof-profile/meson.build index fb22f182..c87581f9 100644 --- a/src/libsysprof-profile/meson.build +++ b/src/libsysprof-profile/meson.build @@ -8,6 +8,7 @@ libsysprof_profile_public_sources = [ libsysprof_profile_private_sources = [ 'mapped-ring-buffer-source.c', 'sysprof-controlfd-instrument.c', + 'sysprof-podman.c', 'sysprof-polkit.c', ] @@ -31,6 +32,7 @@ libsysprof_profile_deps = [ dependency('gio-unix-2.0', version: glib_req_version, required: host_machine.system() != 'windows'), + dependency('json-glib-1.0'), dependency('libdex-1', version: dex_req_version), dependency('polkit-gobject-1', version: polkit_req_version), diff --git a/src/libsysprof-profile/sysprof-podman-private.h b/src/libsysprof-profile/sysprof-podman-private.h new file mode 100644 index 00000000..95d67350 --- /dev/null +++ b/src/libsysprof-profile/sysprof-podman-private.h @@ -0,0 +1,36 @@ +/* sysprof-podman-private.h + * + * Copyright 2020 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 + +typedef struct _SysprofPodman SysprofPodman; + +SysprofPodman *sysprof_podman_snapshot_current_user (void); +gchar **sysprof_podman_get_layers (SysprofPodman *self, + const char *container); +void sysprof_podman_free (SysprofPodman *self); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofPodman, sysprof_podman_free) + +G_END_DECLS diff --git a/src/libsysprof-profile/sysprof-podman.c b/src/libsysprof-profile/sysprof-podman.c new file mode 100644 index 00000000..81076e8c --- /dev/null +++ b/src/libsysprof-profile/sysprof-podman.c @@ -0,0 +1,274 @@ +/* sysprof-podman.c + * + * Copyright 2019 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 + */ + +#define G_LOG_DOMAIN "sysprof-podman" + +#include "config.h" + +#include + +#include "sysprof-podman-private.h" + +struct _SysprofPodman +{ + JsonParser *containers_parser; + JsonParser *layers_parser; + JsonParser *images_parser; +}; + +void +sysprof_podman_free (SysprofPodman *self) +{ + g_clear_object (&self->containers_parser); + g_clear_object (&self->layers_parser); + g_clear_object (&self->images_parser); + g_slice_free (SysprofPodman, self); +} + +static void +load_containers (SysprofPodman *self) +{ + g_autofree char *path = NULL; + + g_assert (self != NULL); + + path = g_build_filename (g_get_user_data_dir (), + "containers", + "storage", + "overlay-containers", + "containers.json", + NULL); + json_parser_load_from_file (self->containers_parser, path, NULL); +} + +static void +load_layers (SysprofPodman *self) +{ + g_autofree char *path = NULL; + + g_assert (self != NULL); + + path = g_build_filename (g_get_user_data_dir (), + "containers", + "storage", + "overlay-layers", + "layers.json", + NULL); + json_parser_load_from_file (self->layers_parser, path, NULL); +} + +static void +load_images (SysprofPodman *self) +{ + g_autofree char *path = NULL; + + g_assert (self != NULL); + + path = g_build_filename (g_get_user_data_dir (), + "containers", + "storage", + "overlay-images", + "images.json", + NULL); + json_parser_load_from_file (self->images_parser, path, NULL); +} + +SysprofPodman * +sysprof_podman_snapshot_current_user (void) +{ + SysprofPodman *self; + + self = g_slice_new0 (SysprofPodman); + self->containers_parser = json_parser_new (); + self->layers_parser = json_parser_new (); + self->images_parser = json_parser_new (); + + load_containers (self); + load_layers (self); + load_images (self); + + return self; +} + +static const char * +find_image_layer (JsonParser *parser, + const char *image) +{ + JsonNode *root; + JsonArray *ar; + guint n_items; + + g_assert (JSON_IS_PARSER (parser)); + g_assert (image != NULL); + + if (!(root = json_parser_get_root (parser)) || + !JSON_NODE_HOLDS_ARRAY (root) || + !(ar = json_node_get_array (root))) + return NULL; + + n_items = json_array_get_length (ar); + + for (guint i = 0; i < n_items; i++) + { + JsonObject *item = json_array_get_object_element (ar, i); + const char *id; + const char *layer; + + if (item == NULL || + !json_object_has_member (item, "id") || + !json_object_has_member (item, "layer") || + !(id = json_object_get_string_member (item, "id")) || + strcmp (id, image) != 0 || + !(layer = json_object_get_string_member (item, "layer"))) + continue; + + return layer; + } + + return NULL; +} + +static const char * +find_parent_layer (JsonParser *parser, + const char *layer, + GHashTable *seen) +{ + JsonNode *root; + JsonArray *ar; + guint n_items; + + g_assert (JSON_IS_PARSER (parser)); + g_assert (layer != NULL); + g_assert (seen != NULL); + + if (!(root = json_parser_get_root (parser)) || + !JSON_NODE_HOLDS_ARRAY (root) || + !(ar = json_node_get_array (root))) + return NULL; + + n_items = json_array_get_length (ar); + + for (guint i = 0; i < n_items; i++) + { + JsonObject *item = json_array_get_object_element (ar, i); + const char *parent; + const char *id; + + if (item == NULL || + !json_object_has_member (item, "id") || + !json_object_has_member (item, "parent") || + !(id = json_object_get_string_member (item, "id")) || + strcmp (id, layer) != 0 || + !(parent = json_object_get_string_member (item, "parent"))) + continue; + + if (g_hash_table_contains (seen, parent)) + return NULL; + + return parent; + } + + return NULL; +} + +static char * +get_layer_dir (const char *layer) +{ + /* We don't use XDG data dir because this might be in a container + * or flatpak environment that doesn't match. And generally, it's + * always .local. + */ + return g_build_filename (g_get_home_dir (), + ".local", + "share", + "containers", + "storage", + "overlay", + layer, + "diff", + NULL); +} + +gchar ** +sysprof_podman_get_layers (SysprofPodman *self, + const char *container) +{ + const char *layer = NULL; + const char *image = NULL; + GHashTable *layers; + JsonNode *root; + JsonArray *ar; + const char **keys; + char **ret; + guint n_items; + + g_return_val_if_fail (self != NULL, NULL); + g_return_val_if_fail (container != NULL, NULL); + + if (!(root = json_parser_get_root (self->containers_parser)) || + !JSON_NODE_HOLDS_ARRAY (root) || + !(ar = json_node_get_array (root))) + return NULL; + + n_items = json_array_get_length (ar); + + /* First try to locate the "layer" identifier for the container + * in question. + */ + for (guint i = 0; i < n_items; i++) + { + JsonObject *item = json_array_get_object_element (ar, i); + const char *item_layer; + const char *id; + + if (item == NULL || + !(id = json_object_get_string_member (item, "id")) || + strcmp (id, container) != 0 || + !(item_layer = json_object_get_string_member (item, "layer"))) + continue; + + layer = item_layer; + image = json_object_get_string_member (item, "image"); + break; + } + + layers = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + + /* Add all our known layers starting from current layer */ + do + g_hash_table_add (layers, get_layer_dir (layer)); + while ((layer = find_parent_layer (self->layers_parser, layer, layers))); + + /* If an image was specified, add its layer */ + if ((layer = find_image_layer (self->images_parser, image))) + { + do + g_hash_table_add (layers, get_layer_dir (layer)); + while ((layer = find_parent_layer (self->layers_parser, layer, layers))); + } + + keys = (const char **)g_hash_table_get_keys_as_array (layers, NULL); + ret = g_strdupv ((char **)keys); + + g_hash_table_unref (layers); + g_free (keys); + + return ret; +} From 598a2b7cf740254713a2b026d8af305e76672d81 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 30 May 2023 17:58:37 -0700 Subject: [PATCH 0312/1030] libsysprof-profile: capture podman overlays and containerenv Additionally keep information about .flatpak-info so that we can reconstruct which symbols are needed without build-id support. --- .../sysprof-linux-instrument.c | 95 ++++++++++++++++++- 1 file changed, 91 insertions(+), 4 deletions(-) diff --git a/src/libsysprof-profile/sysprof-linux-instrument.c b/src/libsysprof-profile/sysprof-linux-instrument.c index f43928cf..ceeffdc9 100644 --- a/src/libsysprof-profile/sysprof-linux-instrument.c +++ b/src/libsysprof-profile/sysprof-linux-instrument.c @@ -23,6 +23,7 @@ #include #include "sysprof-linux-instrument-private.h" +#include "sysprof-podman-private.h" #include "sysprof-recording-private.h" struct _SysprofLinuxInstrument @@ -103,10 +104,82 @@ add_mmaps (SysprofRecording *recording, } } -static void +static DexFuture * +populate_overlays (SysprofRecording *recording, + SysprofPodman *podman, + int pid, + const char *cgroup) +{ + static GRegex *flatpak_regex; + static GRegex *podman_regex; + + g_autoptr(GMatchInfo) flatpak_match = NULL; + g_autoptr(GMatchInfo) podman_match = NULL; + SysprofCaptureWriter *writer; + + g_assert (SYSPROF_IS_RECORDING (recording)); + g_assert (cgroup != NULL); + + if (strcmp (cgroup, "") == 0) + return dex_future_new_for_boolean (TRUE); + + writer = _sysprof_recording_writer (recording); + + /* This function tries to discover the podman container that contains the + * process identified on the host as @pid. We can only do anything with this + * if the pids are in containers that the running user controls (so that we + * can actually access the overlays). + * + * This stuff, and I want to emphasize, is a giant hack. Just like containers + * are on Linux. But if we are really careful, we can make this work for the + * one particular use case I care about, which is podman/toolbox on Fedora + * Workstation/Silverblue. + * + * -- Christian + */ + if G_UNLIKELY (podman_regex == NULL) + { + podman_regex = g_regex_new ("libpod-([a-z0-9]{64})\\.scope", G_REGEX_OPTIMIZE, 0, NULL); + g_assert (podman_regex != NULL); + } + + if (flatpak_regex == NULL) + { + flatpak_regex = g_regex_new ("app-flatpak-([a-zA-Z_\\-\\.]+)-[0-9]+\\.scope", G_REGEX_OPTIMIZE, 0, NULL); + g_assert (flatpak_regex != NULL); + } + + if (g_regex_match (podman_regex, cgroup, 0, &podman_match)) + { + g_autofree char *word = g_match_info_fetch (podman_match, 1); + g_autofree char *path = g_strdup_printf ("/proc/%d/root/run/.containerenv", pid); + g_auto(GStrv) layers = NULL; + + if ((layers = sysprof_podman_get_layers (podman, word))) + { + for (guint i = 0; layers[i]; i++) + sysprof_capture_writer_add_overlay (writer, + SYSPROF_CAPTURE_CURRENT_TIME, + -1, pid, i, layers[i], "/"); + } + + return _sysprof_recording_add_file (recording, path, FALSE); + } + else if (g_regex_match (flatpak_regex, cgroup, 0, &flatpak_match)) + { + g_autofree char *path = g_strdup_printf ("/proc/%d/root/.flatpak-info", pid); + + return _sysprof_recording_add_file (recording, path, FALSE); + } + + return dex_future_new_for_boolean (TRUE); +} + +static DexFuture * add_process_info (SysprofRecording *recording, GVariant *process_info) { + g_autoptr(SysprofPodman) podman = NULL; g_autoptr(GPtrArray) futures = NULL; SysprofCaptureWriter *writer; GVariantIter iter; @@ -116,6 +189,8 @@ add_process_info (SysprofRecording *recording, g_assert (g_variant_is_of_type (process_info, G_VARIANT_TYPE ("aa{sv}"))); writer = _sysprof_recording_writer (recording); + podman = sysprof_podman_snapshot_current_user (); + futures = g_ptr_array_new_with_free_func (dex_unref); /* Loop through all the PIDs the server notified us about */ g_variant_iter_init (&iter, process_info); @@ -170,12 +245,20 @@ add_process_info (SysprofRecording *recording, ignore_inode = strstr (cgroup, "/libpod-") != NULL; add_mmaps (recording, pid, maps, ignore_inode); - // TODO - //sysprof_proc_source_populate_overlays (self, pid, cgroup); + /* We might have overlays that need to be applied to the process + * which can be rather combursome for old-style Podman using + * FUSE overlayfs. + */ + g_ptr_array_add (futures, populate_overlays (recording, podman, pid, cgroup)); skip: g_variant_dict_clear (&dict); } + + if (futures->len > 0) + return dex_future_allv ((DexFuture **)futures->pdata, futures->len); + + return dex_future_new_for_boolean (TRUE); } static DexFuture * @@ -244,7 +327,7 @@ sysprof_linux_instrument_record_fiber (gpointer user_data) /* Add process records for each of the processes discovered */ process_info = g_variant_get_child_value (process_info_reply, 0); - add_process_info (recording, process_info); + dex_await (add_process_info (recording, process_info), NULL); return dex_future_new_for_boolean (TRUE); } @@ -275,6 +358,10 @@ sysprof_linux_instrument_process_started (SysprofInstrument *instrument, mountinfo_path = g_strdup_printf ("/proc/%u/mountinfo", pid); + /* TODO: If we get the process cgroup and keep our saved podman + * state around, we could poopulate overlays for new processes. + */ + return _sysprof_recording_add_file (recording, mountinfo_path, TRUE); } From 52684c7a128de8e1971609e3fc1d80e6eaf1e0c7 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 30 May 2023 18:41:56 -0700 Subject: [PATCH 0313/1030] libsysprof-profile: start on cpu usage instrument The goal here is to do the whole thing on a fiber rather than how we were doing it before. This just gets the counter registration going, but we need to follow up with the parsing/lseek/etc code. --- src/libsysprof-profile/meson.build | 2 + src/libsysprof-profile/sysprof-cpu-usage.c | 220 +++++++++++++++++++ src/libsysprof-profile/sysprof-cpu-usage.h | 42 ++++ src/libsysprof-profile/sysprof-profile.h | 1 + src/libsysprof-profile/tests/test-profiler.c | 2 + 5 files changed, 267 insertions(+) create mode 100644 src/libsysprof-profile/sysprof-cpu-usage.c create mode 100644 src/libsysprof-profile/sysprof-cpu-usage.h diff --git a/src/libsysprof-profile/meson.build b/src/libsysprof-profile/meson.build index c87581f9..2d29d3e5 100644 --- a/src/libsysprof-profile/meson.build +++ b/src/libsysprof-profile/meson.build @@ -1,4 +1,5 @@ libsysprof_profile_public_sources = [ + 'sysprof-cpu-usage.c', 'sysprof-instrument.c', 'sysprof-profiler.c', 'sysprof-recording.c', @@ -16,6 +17,7 @@ libsysprof_profile_public_headers = [ 'sysprof-profile.h', 'sysprof-instrument.h', + 'sysprof-cpu-usage.h', 'sysprof-profiler.h', 'sysprof-recording.h', 'sysprof-spawnable.h', diff --git a/src/libsysprof-profile/sysprof-cpu-usage.c b/src/libsysprof-profile/sysprof-cpu-usage.c new file mode 100644 index 00000000..18e20a57 --- /dev/null +++ b/src/libsysprof-profile/sysprof-cpu-usage.c @@ -0,0 +1,220 @@ +/* sysprof-cpu-usage.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "sysprof-cpu-usage.h" +#include "sysprof-instrument-private.h" +#include "sysprof-recording-private.h" + +struct _SysprofCpuUsage +{ + SysprofInstrument parent_instance; +}; + +struct _SysprofCpuUsageClass +{ + SysprofInstrumentClass parent_class; +}; + +enum { + PROP_0, + N_PROPS +}; + +G_DEFINE_FINAL_TYPE (SysprofCpuUsage, sysprof_cpu_usage, SYSPROF_TYPE_INSTRUMENT) + +static GParamSpec *properties [N_PROPS]; + +typedef struct _Record +{ + SysprofRecording *recording; + DexFuture *cancellable; +} Record; + +static void +record_free (gpointer data) +{ + Record *record = data; + + g_clear_object (&record->recording); + dex_clear (&record->cancellable); + g_free (record); +} + +static DexFuture * +sysprof_cpu_usage_record_fiber (gpointer user_data) +{ + Record *record = user_data; + SysprofCaptureCounter *counters; + SysprofCaptureCounter *counter; + SysprofCaptureWriter *writer; + g_autofd int stat_fd = -1; + guint n_cpu; + + g_assert (record != NULL); + g_assert (SYSPROF_IS_RECORDING (record->recording)); + g_assert (DEX_IS_FUTURE (record->cancellable)); + + writer = _sysprof_recording_writer (record->recording); + n_cpu = g_get_num_processors (); + stat_fd = open ("/proc/stat", O_RDONLY|O_CLOEXEC); + counters = g_alloca (sizeof *counters * ((n_cpu * 2) + 1)); + + /* Create counter information for all of our counters that we will need + * to submit in upcoming parses. + */ + for (guint i = 0; i < n_cpu; i++) + { + guint counter_base = sysprof_capture_writer_request_counter (writer, 2); + + counter = &counters[i*2]; + counter->id = counter_base; + counter->type = SYSPROF_CAPTURE_COUNTER_DOUBLE; + counter->value.vdbl = 0; + g_strlcpy (counter->category, "CPU Percent", sizeof counter->category); + g_snprintf (counter->name, sizeof counter->name, "Total CPU %d", i); + g_snprintf (counter->description, sizeof counter->description, + "Total CPU usage %d", i); + + counter = &counters[i*2+1]; + counter->id = counter_base + 1; + counter->type = SYSPROF_CAPTURE_COUNTER_DOUBLE; + counter->value.vdbl = 0; + g_strlcpy (counter->category, "CPU Frequency", sizeof counter->category); + g_snprintf (counter->name, sizeof counter->name, "CPU %d", i); + g_snprintf (counter->description, sizeof counter->description, + "Frequency of CPU %d", i); + } + + /* Now create our combined counter */ + counter = &counters[n_cpu*2]; + counter->id = sysprof_capture_writer_request_counter (writer, 1); + counter->type = SYSPROF_CAPTURE_COUNTER_DOUBLE; + counter->value.vdbl = 0; + g_strlcpy (counter->category, "CPU Percent", sizeof counter->category); + g_snprintf (counter->name, sizeof counter->name, "Combined"); + g_snprintf (counter->description, sizeof counter->description, "Combined CPU usage"); + + /* Register all the counters as a group */ + sysprof_capture_writer_define_counters (writer, + SYSPROF_CAPTURE_CURRENT_TIME, + -1, + -1, + counters, + n_cpu * 2 + 1); + + g_print ("Registering %d counters\n", n_cpu * 2 + 1); + + return dex_future_new_for_boolean (TRUE); +} + +static DexFuture * +sysprof_cpu_usage_record (SysprofInstrument *instrument, + SysprofRecording *recording, + GCancellable *cancellable) +{ + Record *record; + + g_assert (SYSPROF_IS_CPU_USAGE (instrument)); + g_assert (SYSPROF_IS_RECORDING (recording)); + g_assert (G_IS_CANCELLABLE (cancellable)); + + record = g_new0 (Record, 1); + record->recording = g_object_ref (recording); + record->cancellable = dex_cancellable_new_from_cancellable (cancellable); + + return dex_scheduler_spawn (NULL, 0, + sysprof_cpu_usage_record_fiber, + record, + record_free); +} + +static void +sysprof_cpu_usage_finalize (GObject *object) +{ + SysprofCpuUsage *self = (SysprofCpuUsage *)object; + + G_OBJECT_CLASS (sysprof_cpu_usage_parent_class)->finalize (object); +} + +static void +sysprof_cpu_usage_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofCpuUsage *self = SYSPROF_CPU_USAGE (object); + + switch (prop_id) + { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_cpu_usage_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SysprofCpuUsage *self = SYSPROF_CPU_USAGE (object); + + switch (prop_id) + { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_cpu_usage_class_init (SysprofCpuUsageClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + SysprofInstrumentClass *instrument_class = SYSPROF_INSTRUMENT_CLASS (klass); + + object_class->finalize = sysprof_cpu_usage_finalize; + object_class->get_property = sysprof_cpu_usage_get_property; + object_class->set_property = sysprof_cpu_usage_set_property; + + instrument_class->record = sysprof_cpu_usage_record; +} + +static void +sysprof_cpu_usage_init (SysprofCpuUsage *self) +{ +} + +SysprofInstrument * +sysprof_cpu_usage_new (void) +{ + return g_object_new (SYSPROF_TYPE_CPU_USAGE, NULL); +} diff --git a/src/libsysprof-profile/sysprof-cpu-usage.h b/src/libsysprof-profile/sysprof-cpu-usage.h new file mode 100644 index 00000000..2e980492 --- /dev/null +++ b/src/libsysprof-profile/sysprof-cpu-usage.h @@ -0,0 +1,42 @@ +/* sysprof-cpu-usage.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-instrument.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_CPU_USAGE (sysprof_cpu_usage_get_type()) +#define SYSPROF_IS_CPU_USAGE(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_CPU_USAGE) +#define SYSPROF_CPU_USAGE(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_CPU_USAGE, SysprofCpuUsage) +#define SYSPROF_CPU_USAGE_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_CPU_USAGE, SysprofCpuUsageClass) + +typedef struct _SysprofCpuUsage SysprofCpuUsage; +typedef struct _SysprofCpuUsageClass SysprofCpuUsageClass; + +SYSPROF_AVAILABLE_IN_ALL +GType sysprof_cpu_usage_get_type (void) G_GNUC_CONST; +SYSPROF_AVAILABLE_IN_ALL +SysprofInstrument *sysprof_cpu_usage_new (void); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofCpuUsage, g_object_unref) + +G_END_DECLS diff --git a/src/libsysprof-profile/sysprof-profile.h b/src/libsysprof-profile/sysprof-profile.h index 70facde6..fd383d46 100644 --- a/src/libsysprof-profile/sysprof-profile.h +++ b/src/libsysprof-profile/sysprof-profile.h @@ -25,6 +25,7 @@ G_BEGIN_DECLS #define SYSPROF_PROFILE_INSIDE +# include "sysprof-cpu-usage.h" # include "sysprof-instrument.h" # include "sysprof-profiler.h" # include "sysprof-recording.h" diff --git a/src/libsysprof-profile/tests/test-profiler.c b/src/libsysprof-profile/tests/test-profiler.c index bfc499f5..5d9a7114 100644 --- a/src/libsysprof-profile/tests/test-profiler.c +++ b/src/libsysprof-profile/tests/test-profiler.c @@ -113,6 +113,8 @@ main (int argc, profiler = sysprof_profiler_new (); + sysprof_profiler_add_instrument (profiler, sysprof_cpu_usage_new ()); + sysprof_profiler_record_async (profiler, writer, NULL, record_cb, NULL); g_unix_signal_add (SIGINT, sigint_handler, main_loop); From 03c6c57fab68baa40079604cf4c0768bd4729552 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 30 May 2023 21:29:24 -0700 Subject: [PATCH 0314/1030] contrib: move line reader to contrib This came from Builder, and it's nice to be able to use it statically from multiple libsysprof libraries. --- .../linereader}/line-reader-private.h | 0 contrib/linereader/meson.build | 7 +++++++ contrib/meson.build | 1 + src/libsysprof-analyze/meson.build | 1 + 4 files changed, 9 insertions(+) rename {src/libsysprof-analyze => contrib/linereader}/line-reader-private.h (100%) create mode 100644 contrib/linereader/meson.build diff --git a/src/libsysprof-analyze/line-reader-private.h b/contrib/linereader/line-reader-private.h similarity index 100% rename from src/libsysprof-analyze/line-reader-private.h rename to contrib/linereader/line-reader-private.h diff --git a/contrib/linereader/meson.build b/contrib/linereader/meson.build new file mode 100644 index 00000000..9055b71e --- /dev/null +++ b/contrib/linereader/meson.build @@ -0,0 +1,7 @@ +liblinereader_deps = [ + dependency('gio-2.0', version: glib_req_version), +] + +liblinereader_static_dep = declare_dependency( + include_directories: include_directories('.'), +) diff --git a/contrib/meson.build b/contrib/meson.build index 1b295c8c..574a013a 100644 --- a/contrib/meson.build +++ b/contrib/meson.build @@ -1,2 +1,3 @@ subdir('eggbitset') subdir('elfparser') +subdir('linereader') diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build index 1fcf1972..13659d9a 100644 --- a/src/libsysprof-analyze/meson.build +++ b/src/libsysprof-analyze/meson.build @@ -87,6 +87,7 @@ libsysprof_analyze_deps = [ libeggbitset_static_dep, libelfparser_static_dep, + liblinereader_static_dep, libsysprof_capture_dep, ] From fd1fb68a985a2077dd54313550d6c1251694fb1c Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 30 May 2023 21:29:49 -0700 Subject: [PATCH 0315/1030] libsysprof-profile: add various CPU parsing This still needs some work because the read operations are blocked currently. --- src/libsysprof-profile/meson.build | 1 + src/libsysprof-profile/sysprof-cpu-usage.c | 286 +++++++++++++++++---- 2 files changed, 235 insertions(+), 52 deletions(-) diff --git a/src/libsysprof-profile/meson.build b/src/libsysprof-profile/meson.build index 2d29d3e5..b0bab6b8 100644 --- a/src/libsysprof-profile/meson.build +++ b/src/libsysprof-profile/meson.build @@ -38,6 +38,7 @@ libsysprof_profile_deps = [ dependency('libdex-1', version: dex_req_version), dependency('polkit-gobject-1', version: polkit_req_version), + liblinereader_static_dep, libsysprof_capture_dep, ] diff --git a/src/libsysprof-profile/sysprof-cpu-usage.c b/src/libsysprof-profile/sysprof-cpu-usage.c index 18e20a57..5b4769be 100644 --- a/src/libsysprof-profile/sysprof-cpu-usage.c +++ b/src/libsysprof-profile/sysprof-cpu-usage.c @@ -28,12 +28,17 @@ #include #include +#include #include +#include "line-reader-private.h" + #include "sysprof-cpu-usage.h" #include "sysprof-instrument-private.h" #include "sysprof-recording-private.h" +#define PROC_STAT_BUF_SIZE (4096*4) + struct _SysprofCpuUsage { SysprofInstrument parent_instance; @@ -44,14 +49,30 @@ struct _SysprofCpuUsageClass SysprofInstrumentClass parent_class; }; -enum { - PROP_0, - N_PROPS -}; - G_DEFINE_FINAL_TYPE (SysprofCpuUsage, sysprof_cpu_usage, SYSPROF_TYPE_INSTRUMENT) -static GParamSpec *properties [N_PROPS]; +typedef struct _CpuInfo +{ + int counter_base; + double total; + glong last_user; + glong last_idle; + glong last_system; + glong last_nice; + glong last_iowait; + glong last_irq; + glong last_softirq; + glong last_steal; + glong last_guest; + glong last_guest_nice; +} CpuInfo; + +typedef struct _CpuFreq +{ + gint64 max; + int stat_fd; + char buf[116]; +} CpuFreq; typedef struct _Record { @@ -69,14 +90,48 @@ record_free (gpointer data) g_free (record); } +static void +freq_info_clear (gpointer data) +{ + CpuFreq *cpu_freq = data; + g_clear_fd (&cpu_freq->stat_fd, NULL); +} + +static double +get_cpu_freq (int stat_fd, + guint cpu, + double max, + char *buf, + gssize len) +{ + gint64 val; + + if (stat_fd == -1) + return .0; + + if (len <= 0) + return .0; + + buf[len] = 0; + g_strchug (buf); + val = g_ascii_strtoll (buf, NULL, 10); + + return (double)val / max * 100.; +} + static DexFuture * sysprof_cpu_usage_record_fiber (gpointer user_data) { Record *record = user_data; + g_autoptr(GArray) cpu_info = NULL; + g_autoptr(GArray) freq_info = NULL; + g_autofd int stat_fd = -1; + g_autofree char *read_buffer = NULL; + SysprofCaptureCounterValue *values; SysprofCaptureCounter *counters; SysprofCaptureCounter *counter; SysprofCaptureWriter *writer; - g_autofd int stat_fd = -1; + guint *ids; guint n_cpu; g_assert (record != NULL); @@ -86,7 +141,16 @@ sysprof_cpu_usage_record_fiber (gpointer user_data) writer = _sysprof_recording_writer (record->recording); n_cpu = g_get_num_processors (); stat_fd = open ("/proc/stat", O_RDONLY|O_CLOEXEC); + g_unix_set_fd_nonblocking (stat_fd, TRUE, NULL); + read_buffer = g_malloc (PROC_STAT_BUF_SIZE); + counters = g_alloca (sizeof *counters * ((n_cpu * 2) + 1)); + ids = g_alloca (sizeof *ids * ((n_cpu * 2) + 1)); + values = g_alloca (sizeof *values * ((n_cpu * 2) + 1)); + + cpu_info = g_array_new (FALSE, TRUE, sizeof (CpuInfo)); + freq_info = g_array_new (FALSE, TRUE, sizeof (CpuFreq)); + g_array_set_clear_func (freq_info, freq_info_clear); /* Create counter information for all of our counters that we will need * to submit in upcoming parses. @@ -94,6 +158,13 @@ sysprof_cpu_usage_record_fiber (gpointer user_data) for (guint i = 0; i < n_cpu; i++) { guint counter_base = sysprof_capture_writer_request_counter (writer, 2); + g_autofree char *max_path = g_strdup_printf ("/sys/devices/system/cpu/cpu%u/cpufreq/scaling_max_freq", i); + g_autofree char *cur_path = g_strdup_printf ("/sys/devices/system/cpu/cpu%u/cpufreq/scaling_cur_freq", i); + g_autofree char *max_value = NULL; + CpuFreq cf; + + ids[n_cpu*2] = counter_base; + ids[n_cpu*2+1] = counter_base + 1; counter = &counters[i*2]; counter->id = counter_base; @@ -112,11 +183,21 @@ sysprof_cpu_usage_record_fiber (gpointer user_data) g_snprintf (counter->name, sizeof counter->name, "CPU %d", i); g_snprintf (counter->description, sizeof counter->description, "Frequency of CPU %d", i); + + cf.stat_fd = open (cur_path, O_RDONLY|O_CLOEXEC); + g_unix_set_fd_nonblocking (cf.stat_fd, TRUE, NULL); + cf.buf[0] = 0; + if (g_file_get_contents (max_path, &max_value, NULL, NULL)) + cf.max = g_ascii_strtoll (max_value, NULL, 10); + else + cf.max = 0; + g_array_append_val (freq_info, cf); } /* Now create our combined counter */ + ids[n_cpu*2] = sysprof_capture_writer_request_counter (writer, 1); counter = &counters[n_cpu*2]; - counter->id = sysprof_capture_writer_request_counter (writer, 1); + counter->id = ids[n_cpu*2]; counter->type = SYSPROF_CAPTURE_COUNTER_DOUBLE; counter->value.vdbl = 0; g_strlcpy (counter->category, "CPU Percent", sizeof counter->category); @@ -131,7 +212,151 @@ sysprof_cpu_usage_record_fiber (gpointer user_data) counters, n_cpu * 2 + 1); - g_print ("Registering %d counters\n", n_cpu * 2 + 1); + for (;;) + { + g_autoptr(GPtrArray) futures = g_ptr_array_new_with_free_func (dex_unref); + g_autoptr(DexFuture) cpu_future = NULL; + LineReader reader; + glong total_usage = 0; + gsize line_len; + char *line; + + g_print ("parsing\n"); + + /* First collect all our reads and then wait for them to finish before + * parsing in a pass. With io_uring, this lets us coalesce all the lseek + * and reads into a single set of iops. + */ + for (guint i = 0; i < n_cpu; i++) + { + CpuFreq *cf = &g_array_index (freq_info, CpuFreq, i); + + g_ptr_array_add (futures, dex_aio_read (NULL, cf->stat_fd, cf->buf, sizeof cf->buf - 1, 0)); + } + cpu_future = dex_aio_read (NULL, stat_fd, read_buffer, PROC_STAT_BUF_SIZE, 0); + g_ptr_array_add (futures, dex_ref (cpu_future)); + g_ptr_array_add (futures, dex_ref (record->cancellable)); + if (!dex_await (dex_future_allv ((DexFuture **)futures->pdata, futures->len), NULL)) + break; + + g_print ("Waiting for completions\n"); + + /* Now parse all the contents of the stat files which should be + * populated in the various files. + */ + line_reader_init (&reader, read_buffer, dex_await_int64 (cpu_future, NULL)); + while ((line = line_reader_next (&reader, &line_len))) + { + CpuInfo *ci; + char cpu[64]; + glong user; + glong sys; + glong nice; + glong idle; + int id; + int ret; + glong iowait; + glong irq; + glong softirq; + glong steal; + glong guest; + glong guest_nice; + glong user_calc; + glong system_calc; + glong nice_calc; + glong idle_calc; + glong iowait_calc; + glong irq_calc; + glong softirq_calc; + glong steal_calc; + glong guest_calc; + glong guest_nice_calc; + glong total; + + line[line_len] = 0; + + /* CPU lines come first, short-circuit after */ + if (!g_str_has_prefix (line, "cpu")) + break; + + /* First line is "cpu ..." */ + if (!g_ascii_isdigit (line[3])) + continue; + + /* Parse the various counters in order */ + user = nice = sys = idle = id = 0; + ret = sscanf (line, "%s %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld", + cpu, &user, &nice, &sys, &idle, + &iowait, &irq, &softirq, &steal, &guest, &guest_nice); + if (ret != 11) + continue; + + /* Get the CPU identifier */ + ret = sscanf (cpu, "cpu%d", &id); + if (ret != 1 || id < 0 || id >= n_cpu) + continue; + + ci = &g_array_index (cpu_info, CpuInfo, id); + + user_calc = user - ci->last_user; + nice_calc = nice - ci->last_nice; + system_calc = sys - ci->last_system; + idle_calc = idle - ci->last_idle; + iowait_calc = iowait - ci->last_iowait; + irq_calc = irq - ci->last_irq; + softirq_calc = softirq - ci->last_softirq; + steal_calc = steal - ci->last_steal; + guest_calc = guest - ci->last_guest; + guest_nice_calc = guest_nice - ci->last_guest_nice; + + total = user_calc + nice_calc + system_calc + idle_calc + iowait_calc + irq_calc + softirq_calc + steal_calc + guest_calc + guest_nice_calc; + ci->total = ((total - idle_calc) / (double)total) * 100.; + + ci->last_user = user; + ci->last_nice = nice; + ci->last_idle = idle; + ci->last_system = sys; + ci->last_iowait = iowait; + ci->last_irq = irq; + ci->last_softirq = softirq; + ci->last_steal = steal; + ci->last_guest = guest; + ci->last_guest_nice = guest_nice; + } + + /* Publish counters to the capture file */ + for (guint i = 0; i < n_cpu; i++) + { + const CpuInfo *ci = &g_array_index (cpu_info, CpuInfo, i); + CpuFreq *cf = &g_array_index (freq_info, CpuFreq, i); + DexFuture *freq_future = g_ptr_array_index (futures, i); + gssize len = dex_await_int64 (dex_ref (freq_future), NULL); + + values[n_cpu*i].vdbl = ci->total; + values[n_cpu*i+1].vdbl = get_cpu_freq (cf->stat_fd, i, cf->max, cf->buf, len); + + total_usage += ci->total; + } + + values[n_cpu*2].vdbl = total_usage / (double)n_cpu; + sysprof_capture_writer_set_counters (writer, + SYSPROF_CAPTURE_CURRENT_TIME, + -1, + -1, + ids, + values, + n_cpu * 2 + 1); + + g_print ("adding counters\n"); + + /* Wait for cancellation or ½ second */ + dex_await (dex_future_any (dex_ref (record->cancellable), + dex_timeout_new_usec (G_USEC_PER_SEC / 2), + NULL), + NULL); + if (dex_future_get_status (record->cancellable) != DEX_FUTURE_STATUS_PENDING) + break; + } return dex_future_new_for_boolean (TRUE); } @@ -157,54 +382,11 @@ sysprof_cpu_usage_record (SysprofInstrument *instrument, record_free); } -static void -sysprof_cpu_usage_finalize (GObject *object) -{ - SysprofCpuUsage *self = (SysprofCpuUsage *)object; - - G_OBJECT_CLASS (sysprof_cpu_usage_parent_class)->finalize (object); -} - -static void -sysprof_cpu_usage_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - SysprofCpuUsage *self = SYSPROF_CPU_USAGE (object); - - switch (prop_id) - { - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_cpu_usage_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - SysprofCpuUsage *self = SYSPROF_CPU_USAGE (object); - - switch (prop_id) - { - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - static void sysprof_cpu_usage_class_init (SysprofCpuUsageClass *klass) { - GObjectClass *object_class = G_OBJECT_CLASS (klass); SysprofInstrumentClass *instrument_class = SYSPROF_INSTRUMENT_CLASS (klass); - object_class->finalize = sysprof_cpu_usage_finalize; - object_class->get_property = sysprof_cpu_usage_get_property; - object_class->set_property = sysprof_cpu_usage_set_property; - instrument_class->record = sysprof_cpu_usage_record; } From 5389d6ac5136316dc7bda068c6ac81515638d13f Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 31 May 2023 09:58:51 -0700 Subject: [PATCH 0316/1030] libsysprof-profile: fix logic of future checks --- src/libsysprof-profile/sysprof-cpu-usage.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/libsysprof-profile/sysprof-cpu-usage.c b/src/libsysprof-profile/sysprof-cpu-usage.c index 5b4769be..1d5ea035 100644 --- a/src/libsysprof-profile/sysprof-cpu-usage.c +++ b/src/libsysprof-profile/sysprof-cpu-usage.c @@ -149,6 +149,8 @@ sysprof_cpu_usage_record_fiber (gpointer user_data) values = g_alloca (sizeof *values * ((n_cpu * 2) + 1)); cpu_info = g_array_new (FALSE, TRUE, sizeof (CpuInfo)); + g_array_set_size (cpu_info, n_cpu); + freq_info = g_array_new (FALSE, TRUE, sizeof (CpuFreq)); g_array_set_clear_func (freq_info, freq_info_clear); @@ -235,8 +237,10 @@ sysprof_cpu_usage_record_fiber (gpointer user_data) } cpu_future = dex_aio_read (NULL, stat_fd, read_buffer, PROC_STAT_BUF_SIZE, 0); g_ptr_array_add (futures, dex_ref (cpu_future)); - g_ptr_array_add (futures, dex_ref (record->cancellable)); - if (!dex_await (dex_future_allv ((DexFuture **)futures->pdata, futures->len), NULL)) + if (!dex_await (dex_future_any (dex_ref (record->cancellable), + dex_future_allv ((DexFuture **)futures->pdata, futures->len), + NULL), + NULL)) break; g_print ("Waiting for completions\n"); @@ -350,9 +354,9 @@ sysprof_cpu_usage_record_fiber (gpointer user_data) g_print ("adding counters\n"); /* Wait for cancellation or ½ second */ - dex_await (dex_future_any (dex_ref (record->cancellable), - dex_timeout_new_usec (G_USEC_PER_SEC / 2), - NULL), + dex_await (dex_future_first (dex_ref (record->cancellable), + dex_timeout_new_usec (G_USEC_PER_SEC / 2), + NULL), NULL); if (dex_future_get_status (record->cancellable) != DEX_FUTURE_STATUS_PENDING) break; From e195b8961c76304e4d62768448217ce647af8a83 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 31 May 2023 10:11:47 -0700 Subject: [PATCH 0317/1030] libsysprof-profile: fix index of cpu ids --- src/libsysprof-profile/sysprof-cpu-usage.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libsysprof-profile/sysprof-cpu-usage.c b/src/libsysprof-profile/sysprof-cpu-usage.c index 1d5ea035..52e24717 100644 --- a/src/libsysprof-profile/sysprof-cpu-usage.c +++ b/src/libsysprof-profile/sysprof-cpu-usage.c @@ -165,8 +165,8 @@ sysprof_cpu_usage_record_fiber (gpointer user_data) g_autofree char *max_value = NULL; CpuFreq cf; - ids[n_cpu*2] = counter_base; - ids[n_cpu*2+1] = counter_base + 1; + ids[i*2] = counter_base; + ids[i*2+1] = counter_base + 1; counter = &counters[i*2]; counter->id = counter_base; From 302a772c8d975b14f859d3740e75fae7ec5b6968 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 31 May 2023 10:12:09 -0700 Subject: [PATCH 0318/1030] libsysprof-profile: fix await usage --- src/libsysprof-profile/sysprof-cpu-usage.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libsysprof-profile/sysprof-cpu-usage.c b/src/libsysprof-profile/sysprof-cpu-usage.c index 52e24717..09e6427f 100644 --- a/src/libsysprof-profile/sysprof-cpu-usage.c +++ b/src/libsysprof-profile/sysprof-cpu-usage.c @@ -237,9 +237,9 @@ sysprof_cpu_usage_record_fiber (gpointer user_data) } cpu_future = dex_aio_read (NULL, stat_fd, read_buffer, PROC_STAT_BUF_SIZE, 0); g_ptr_array_add (futures, dex_ref (cpu_future)); - if (!dex_await (dex_future_any (dex_ref (record->cancellable), - dex_future_allv ((DexFuture **)futures->pdata, futures->len), - NULL), + if (!dex_await (dex_future_first (dex_ref (record->cancellable), + dex_future_allv ((DexFuture **)futures->pdata, futures->len), + NULL), NULL)) break; @@ -248,7 +248,7 @@ sysprof_cpu_usage_record_fiber (gpointer user_data) /* Now parse all the contents of the stat files which should be * populated in the various files. */ - line_reader_init (&reader, read_buffer, dex_await_int64 (cpu_future, NULL)); + line_reader_init (&reader, read_buffer, dex_await_int64 (dex_ref (cpu_future), NULL)); while ((line = line_reader_next (&reader, &line_len))) { CpuInfo *ci; From bff99f1f63cafb355c905d0d6a4c513943b18a9b Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 31 May 2023 10:12:17 -0700 Subject: [PATCH 0319/1030] libsysprof-profile: remove debug code --- src/libsysprof-profile/sysprof-cpu-usage.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/libsysprof-profile/sysprof-cpu-usage.c b/src/libsysprof-profile/sysprof-cpu-usage.c index 09e6427f..7fae3677 100644 --- a/src/libsysprof-profile/sysprof-cpu-usage.c +++ b/src/libsysprof-profile/sysprof-cpu-usage.c @@ -223,8 +223,6 @@ sysprof_cpu_usage_record_fiber (gpointer user_data) gsize line_len; char *line; - g_print ("parsing\n"); - /* First collect all our reads and then wait for them to finish before * parsing in a pass. With io_uring, this lets us coalesce all the lseek * and reads into a single set of iops. @@ -243,8 +241,6 @@ sysprof_cpu_usage_record_fiber (gpointer user_data) NULL)) break; - g_print ("Waiting for completions\n"); - /* Now parse all the contents of the stat files which should be * populated in the various files. */ @@ -351,8 +347,6 @@ sysprof_cpu_usage_record_fiber (gpointer user_data) values, n_cpu * 2 + 1); - g_print ("adding counters\n"); - /* Wait for cancellation or ½ second */ dex_await (dex_future_first (dex_ref (record->cancellable), dex_timeout_new_usec (G_USEC_PER_SEC / 2), From e3404dd0ca61f485c0c89a5563b6c28e49e677a3 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 31 May 2023 10:16:51 -0700 Subject: [PATCH 0320/1030] libsysprof-profile: update time range for recording That way we don't have gaps at the edges. --- src/libsysprof-profile/sysprof-recording.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/libsysprof-profile/sysprof-recording.c b/src/libsysprof-profile/sysprof-recording.c index a2f2d590..bfbec64f 100644 --- a/src/libsysprof-profile/sysprof-recording.c +++ b/src/libsysprof-profile/sysprof-recording.c @@ -75,6 +75,7 @@ sysprof_recording_fiber (gpointer user_data) g_autoptr(GCancellable) cancellable = NULL; g_autoptr(DexFuture) record = NULL; g_autoptr(GError) error = NULL; + gint64 begin_time; g_assert (SYSPROF_IS_RECORDING (self)); @@ -94,6 +95,9 @@ sysprof_recording_fiber (gpointer user_data) /* Ask instruments to start recording and stop if cancelled. */ record = _sysprof_instruments_record (self->instruments, self, cancellable); + /* Now take our begin time now that all instruments are notified */ + begin_time = SYSPROF_CAPTURE_CURRENT_TIME; + /* Wait for messages on our channel or the recording to complete */ for (;;) { @@ -129,6 +133,9 @@ stop_recording: */ g_cancellable_cancel (cancellable); + /* Update start/end times to be the "running time" */ + _sysprof_capture_writer_set_time_range (self->writer, begin_time, SYSPROF_CAPTURE_CURRENT_TIME); + if (error != NULL) return dex_future_new_for_error (g_steal_pointer (&error)); From fd40e940d311faacfcd5423a9ae0b04f6b548aea Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 31 May 2023 22:43:33 -0700 Subject: [PATCH 0321/1030] libsysprof-profile: add network usage instrument --- src/libsysprof-profile/meson.build | 2 + .../sysprof-network-usage.c | 368 ++++++++++++++++++ .../sysprof-network-usage.h | 42 ++ src/libsysprof-profile/sysprof-profile.h | 1 + src/libsysprof-profile/tests/test-profiler.c | 1 + 5 files changed, 414 insertions(+) create mode 100644 src/libsysprof-profile/sysprof-network-usage.c create mode 100644 src/libsysprof-profile/sysprof-network-usage.h diff --git a/src/libsysprof-profile/meson.build b/src/libsysprof-profile/meson.build index b0bab6b8..957a3509 100644 --- a/src/libsysprof-profile/meson.build +++ b/src/libsysprof-profile/meson.build @@ -1,6 +1,7 @@ libsysprof_profile_public_sources = [ 'sysprof-cpu-usage.c', 'sysprof-instrument.c', + 'sysprof-network-usage.c', 'sysprof-profiler.c', 'sysprof-recording.c', 'sysprof-spawnable.c', @@ -18,6 +19,7 @@ libsysprof_profile_public_headers = [ 'sysprof-instrument.h', 'sysprof-cpu-usage.h', + 'sysprof-network-usage.h', 'sysprof-profiler.h', 'sysprof-recording.h', 'sysprof-spawnable.h', diff --git a/src/libsysprof-profile/sysprof-network-usage.c b/src/libsysprof-profile/sysprof-network-usage.c new file mode 100644 index 00000000..0db5a966 --- /dev/null +++ b/src/libsysprof-profile/sysprof-network-usage.c @@ -0,0 +1,368 @@ +/* sysprof-network-usage.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "line-reader-private.h" + +#include "sysprof-network-usage.h" +#include "sysprof-instrument-private.h" +#include "sysprof-recording-private.h" + +struct _SysprofNetworkUsage +{ + SysprofInstrument parent_instance; +}; + +struct _SysprofNetworkUsageClass +{ + SysprofInstrumentClass parent_class; +}; + +typedef struct _DeviceUsage +{ + /* Counter IDs */ + guint rx_bytes_id; + guint tx_bytes_id; + gchar iface[32]; + gint64 rx_bytes; + gint64 rx_packets; + gint64 rx_errors; + gint64 rx_dropped; + gint64 rx_fifo; + gint64 rx_frame; + gint64 rx_compressed; + gint64 rx_multicast; + gint64 tx_bytes; + gint64 tx_packets; + gint64 tx_errors; + gint64 tx_dropped; + gint64 tx_fifo; + gint64 tx_collisions; + gint64 tx_carrier; + gint64 tx_compressed; +} DeviceUsage; + +G_DEFINE_FINAL_TYPE (SysprofNetworkUsage, sysprof_network_usage, SYSPROF_TYPE_INSTRUMENT) + +typedef struct _Record +{ + SysprofRecording *recording; + DexFuture *cancellable; +} Record; + +static void +record_free (gpointer data) +{ + Record *record = data; + + g_clear_object (&record->recording); + dex_clear (&record->cancellable); + g_free (record); +} + +static DeviceUsage * +find_device_by_name (GArray *ar, + const char *name) +{ + for (guint i = 0; i < ar->len; i++) + { + DeviceUsage *dev = &g_array_index (ar, DeviceUsage, i); + + if (g_strcmp0 (name, dev->iface) == 0) + return dev; + } + + return NULL; +} + +static DexFuture * +sysprof_network_usage_record_fiber (gpointer user_data) +{ + char buf[4096*2]; + Record *record = user_data; + SysprofCaptureCounterValue *values; + g_autoptr(GArray) devices = NULL; + g_autoptr(GError) error = NULL; + SysprofCaptureWriter *writer; + SysprofCaptureCounter ctr[2] = {0}; + g_autofd int stat_fd = -1; + LineReader reader; + guint *ids; + guint combined_rx_id; + guint combined_tx_id; + gssize n_read; + gsize line_len; + guint lineno; + char *line; + + g_assert (record != NULL); + g_assert (SYSPROF_IS_RECORDING (record->recording)); + g_assert (DEX_IS_FUTURE (record->cancellable)); + + writer = _sysprof_recording_writer (record->recording); + + if (-1 == (stat_fd = open ("/proc/net/dev", O_RDONLY|O_CLOEXEC))) + return dex_future_new_reject (G_IO_ERROR, + g_io_error_from_errno (errno), + "%s", g_strerror (errno)); + + devices = g_array_new (FALSE, FALSE, sizeof (DeviceUsage)); + + combined_rx_id = sysprof_capture_writer_request_counter (writer, 1); + combined_tx_id = sysprof_capture_writer_request_counter (writer, 1); + + g_strlcpy (ctr[0].category, "Network", sizeof ctr[0].category); + g_strlcpy (ctr[0].name, "RX Bytes", sizeof ctr[0].name); + g_strlcpy (ctr[0].description, "Combined", sizeof ctr[0].description); + ctr[0].id = combined_rx_id; + ctr[0].type = SYSPROF_CAPTURE_COUNTER_INT64; + ctr[0].value.v64 = 0; + + g_strlcpy (ctr[1].category, "Network", sizeof ctr[1].category); + g_strlcpy (ctr[1].name, "TX Bytes", sizeof ctr[1].name); + g_strlcpy (ctr[1].description, "Combined", sizeof ctr[1].description); + ctr[1].id = combined_tx_id; + ctr[1].type = SYSPROF_CAPTURE_COUNTER_INT64; + ctr[1].value.v64 = 0; + + sysprof_capture_writer_define_counters (writer, + SYSPROF_CAPTURE_CURRENT_TIME, + -1, + -1, + ctr, G_N_ELEMENTS (ctr)); + + n_read = dex_await_int64 (dex_aio_read (NULL, stat_fd, buf, sizeof buf, 0), &error); + if (n_read <= 0) + return dex_future_new_reject (G_IO_ERROR, + g_io_error_from_errno (errno), + "%s", g_strerror (errno)); + + lineno = 0; + line_reader_init (&reader, buf, n_read); + while ((line = line_reader_next (&reader, &line_len))) + { + DeviceUsage dev = {0}; + g_autofree char *rx = NULL; + g_autofree char *tx = NULL; + char *ptr = line; + char *name; + + line[line_len] = 0; + + if (lineno++ < 2) + continue; + + for (; *ptr && g_ascii_isspace (*ptr); ptr++) { /* Do Nothing */ } + name = ptr; + for (; *ptr && *ptr != ':'; ptr++) { /* Do Nothing */ } + *ptr = 0; + + rx = g_strdup_printf ("RX Bytes (%s)", name); + tx = g_strdup_printf ("TX Bytes (%s)", name); + + g_strlcpy (ctr[0].category, "Network", sizeof ctr[0].category); + g_strlcpy (ctr[0].name, rx, sizeof ctr[0].name); + g_strlcpy (ctr[0].description, name, sizeof ctr[0].description); + ctr[0].id = sysprof_capture_writer_request_counter (writer, 1); + ctr[0].type = SYSPROF_CAPTURE_COUNTER_INT64; + ctr[0].value.v64 = 0; + + g_strlcpy (ctr[1].category, "Network", sizeof ctr[1].category); + g_strlcpy (ctr[1].name, tx, sizeof ctr[1].name); + g_strlcpy (ctr[1].description, name, sizeof ctr[1].description); + ctr[1].id = sysprof_capture_writer_request_counter (writer, 1); + ctr[1].type = SYSPROF_CAPTURE_COUNTER_INT64; + ctr[1].value.v64 = 0; + + sysprof_capture_writer_define_counters (writer, + SYSPROF_CAPTURE_CURRENT_TIME, + -1, + -1, + ctr, G_N_ELEMENTS (ctr)); + + dev.rx_bytes_id = ctr[0].id; + dev.tx_bytes_id = ctr[0].id; + g_strlcpy (dev.iface, name, sizeof dev.iface); + + g_array_append_val (devices, dev); + } + + values = g_alloca (sizeof (SysprofCaptureCounterValue) * (devices->len + 2)); + ids = g_alloca (sizeof (guint) * (devices->len + 2)); + ids[0] = combined_rx_id; + ids[1] = combined_tx_id; + for (guint i = 0; i < devices->len; i++) + { + const DeviceUsage *dev = &g_array_index (devices, DeviceUsage, i); + ids[(i+1)*2] = dev->rx_bytes_id; + ids[(i+1)*2+1] = dev->tx_bytes_id; + } + + for (;;) + { + DeviceUsage *first = &g_array_index (devices, DeviceUsage, 0); + gint64 combined_rx = 0; + gint64 combined_tx = 0; + + n_read = dex_await_int64 (dex_aio_read (NULL, stat_fd, buf, sizeof buf, 0), &error); + if (n_read <= 0) + break; + + lineno = 0; + line_reader_init (&reader, buf, n_read); + while ((line = line_reader_next (&reader, &line_len))) + { + DeviceUsage *dev; + guint index; + char *name; + char *ptr = line; + + line[line_len] = 0; + + if (lineno++ < 2) + continue; + + for (; *ptr && g_ascii_isspace (*ptr); ptr++) { /* Do Nothing */ } + name = ptr; + for (; *ptr && *ptr != ':'; ptr++) { /* Do Nothing */ } + *ptr = 0; + + if (!(dev = find_device_by_name (devices, name))) + continue; + + ptr++; + + sscanf (ptr, + "%"G_GINT64_FORMAT + " %"G_GINT64_FORMAT + " %"G_GINT64_FORMAT + " %"G_GINT64_FORMAT + " %"G_GINT64_FORMAT + " %"G_GINT64_FORMAT + " %"G_GINT64_FORMAT + " %"G_GINT64_FORMAT + " %"G_GINT64_FORMAT + " %"G_GINT64_FORMAT + " %"G_GINT64_FORMAT + " %"G_GINT64_FORMAT + " %"G_GINT64_FORMAT + " %"G_GINT64_FORMAT + " %"G_GINT64_FORMAT + " %"G_GINT64_FORMAT, + &dev->rx_bytes, + &dev->rx_packets, + &dev->rx_errors, + &dev->rx_dropped, + &dev->rx_fifo, + &dev->rx_frame, + &dev->rx_compressed, + &dev->rx_multicast, + &dev->tx_bytes, + &dev->tx_packets, + &dev->tx_errors, + &dev->tx_dropped, + &dev->tx_fifo, + &dev->tx_collisions, + &dev->tx_carrier, + &dev->tx_compressed); + + combined_rx += dev->rx_bytes; + combined_tx += dev->tx_bytes; + + index = dev - first; + + values[(index+1)*2].v64 = dev->rx_bytes; + values[(index+1)*2+1].v64 = dev->tx_bytes; + } + + values[0].v64 = combined_rx; + values[1].v64 = combined_tx; + + sysprof_capture_writer_set_counters (writer, + SYSPROF_CAPTURE_CURRENT_TIME, + -1, + -1, + ids, + values, + (devices->len + 1) * 2); + + /* Wait for cancellation or ½ second */ + dex_await (dex_future_first (dex_ref (record->cancellable), + dex_timeout_new_usec (G_USEC_PER_SEC / 2), + NULL), + NULL); + if (dex_future_get_status (record->cancellable) != DEX_FUTURE_STATUS_PENDING) + break; + } + + return dex_future_new_for_boolean (TRUE); +} + +static DexFuture * +sysprof_network_usage_record (SysprofInstrument *instrument, + SysprofRecording *recording, + GCancellable *cancellable) +{ + Record *record; + + g_assert (SYSPROF_IS_NETWORK_USAGE (instrument)); + g_assert (SYSPROF_IS_RECORDING (recording)); + g_assert (G_IS_CANCELLABLE (cancellable)); + + record = g_new0 (Record, 1); + record->recording = g_object_ref (recording); + record->cancellable = dex_cancellable_new_from_cancellable (cancellable); + + return dex_scheduler_spawn (NULL, 0, + sysprof_network_usage_record_fiber, + record, + record_free); +} + +static void +sysprof_network_usage_class_init (SysprofNetworkUsageClass *klass) +{ + SysprofInstrumentClass *instrument_class = SYSPROF_INSTRUMENT_CLASS (klass); + + instrument_class->record = sysprof_network_usage_record; +} + +static void +sysprof_network_usage_init (SysprofNetworkUsage *self) +{ +} + +SysprofInstrument * +sysprof_network_usage_new (void) +{ + return g_object_new (SYSPROF_TYPE_NETWORK_USAGE, NULL); +} diff --git a/src/libsysprof-profile/sysprof-network-usage.h b/src/libsysprof-profile/sysprof-network-usage.h new file mode 100644 index 00000000..5cd0cb5d --- /dev/null +++ b/src/libsysprof-profile/sysprof-network-usage.h @@ -0,0 +1,42 @@ +/* sysprof-network-usage.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-instrument.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_NETWORK_USAGE (sysprof_network_usage_get_type()) +#define SYSPROF_IS_NETWORK_USAGE(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_NETWORK_USAGE) +#define SYSPROF_NETWORK_USAGE(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_NETWORK_USAGE, SysprofNetworkUsage) +#define SYSPROF_NETWORK_USAGE_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_NETWORK_USAGE, SysprofNetworkUsageClass) + +typedef struct _SysprofNetworkUsage SysprofNetworkUsage; +typedef struct _SysprofNetworkUsageClass SysprofNetworkUsageClass; + +SYSPROF_AVAILABLE_IN_ALL +GType sysprof_network_usage_get_type (void) G_GNUC_CONST; +SYSPROF_AVAILABLE_IN_ALL +SysprofInstrument *sysprof_network_usage_new (void); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofNetworkUsage, g_object_unref) + +G_END_DECLS diff --git a/src/libsysprof-profile/sysprof-profile.h b/src/libsysprof-profile/sysprof-profile.h index fd383d46..74f6e8ea 100644 --- a/src/libsysprof-profile/sysprof-profile.h +++ b/src/libsysprof-profile/sysprof-profile.h @@ -27,6 +27,7 @@ G_BEGIN_DECLS #define SYSPROF_PROFILE_INSIDE # include "sysprof-cpu-usage.h" # include "sysprof-instrument.h" +# include "sysprof-network-usage.h" # include "sysprof-profiler.h" # include "sysprof-recording.h" # include "sysprof-spawnable.h" diff --git a/src/libsysprof-profile/tests/test-profiler.c b/src/libsysprof-profile/tests/test-profiler.c index 5d9a7114..8e7780a8 100644 --- a/src/libsysprof-profile/tests/test-profiler.c +++ b/src/libsysprof-profile/tests/test-profiler.c @@ -114,6 +114,7 @@ main (int argc, profiler = sysprof_profiler_new (); sysprof_profiler_add_instrument (profiler, sysprof_cpu_usage_new ()); + sysprof_profiler_add_instrument (profiler, sysprof_network_usage_new ()); sysprof_profiler_record_async (profiler, writer, NULL, record_cb, NULL); From 60ecde017fdd14fc108d536b5e4215911ec0fe01 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 2 Jun 2023 12:58:20 -0700 Subject: [PATCH 0322/1030] libsysprof-profile: add wrappers for instrument vfuncs For maintainability, I'd prefer to keep these calls going through a wrapper function. They get inlined likely by the compiler anyway. --- src/libsysprof-profile/sysprof-instrument.c | 80 ++++++++++++++++++--- 1 file changed, 70 insertions(+), 10 deletions(-) diff --git a/src/libsysprof-profile/sysprof-instrument.c b/src/libsysprof-profile/sysprof-instrument.c index 37eba55d..790efa94 100644 --- a/src/libsysprof-profile/sysprof-instrument.c +++ b/src/libsysprof-profile/sysprof-instrument.c @@ -68,6 +68,72 @@ sysprof_instrument_init (SysprofInstrument *self) { } +static char ** +_sysprof_instrument_list_required_policy (SysprofInstrument *self) +{ + g_assert (SYSPROF_IS_INSTRUMENT (self)); + + if (SYSPROF_INSTRUMENT_GET_CLASS (self)->list_required_policy) + return SYSPROF_INSTRUMENT_GET_CLASS (self)->list_required_policy (self); + + return NULL; +} + +static DexFuture * +_sysprof_instrument_prepare (SysprofInstrument *self, + SysprofRecording *recording) +{ + g_assert (SYSPROF_IS_INSTRUMENT (self)); + g_assert (SYSPROF_IS_RECORDING (recording)); + + if (SYSPROF_INSTRUMENT_GET_CLASS (self)->prepare) + return SYSPROF_INSTRUMENT_GET_CLASS (self)->prepare (self, recording); + + return dex_future_new_for_boolean (TRUE); +} + +static DexFuture * +_sysprof_instrument_record (SysprofInstrument *self, + SysprofRecording *recording, + GCancellable *cancellable) +{ + g_assert (SYSPROF_IS_INSTRUMENT (self)); + g_assert (SYSPROF_IS_RECORDING (recording)); + g_assert (G_IS_CANCELLABLE (cancellable)); + + if (SYSPROF_INSTRUMENT_GET_CLASS (self)->record) + return SYSPROF_INSTRUMENT_GET_CLASS (self)->record (self, recording, cancellable); + + return dex_future_new_for_boolean (TRUE); +} + +static DexFuture * +_sysprof_instrument_augment (SysprofInstrument *self, + SysprofRecording *recording) +{ + g_assert (SYSPROF_IS_INSTRUMENT (self)); + g_assert (SYSPROF_IS_RECORDING (recording)); + + if (SYSPROF_INSTRUMENT_GET_CLASS (self)->augment) + return SYSPROF_INSTRUMENT_GET_CLASS (self)->augment (self, recording); + + return dex_future_new_for_boolean (TRUE); +} + +static DexFuture * +_sysprof_instrument_process_started (SysprofInstrument *self, + SysprofRecording *recording, + int pid) +{ + g_assert (SYSPROF_IS_INSTRUMENT (self)); + g_assert (SYSPROF_IS_RECORDING (recording)); + + if (SYSPROF_INSTRUMENT_GET_CLASS (self)->process_started) + return SYSPROF_INSTRUMENT_GET_CLASS (self)->process_started (self, recording, pid); + + return dex_future_new_for_boolean (TRUE); +} + static char ** _sysprof_instruments_list_required_policy (GPtrArray *instruments) { @@ -80,7 +146,7 @@ _sysprof_instruments_list_required_policy (GPtrArray *instruments) for (guint i = 0; i < instruments->len; i++) { SysprofInstrument *instrument = g_ptr_array_index (instruments, i); - g_auto(GStrv) policy = SYSPROF_INSTRUMENT_GET_CLASS (instrument)->list_required_policy (instrument); + g_auto(GStrv) policy = _sysprof_instrument_list_required_policy (instrument); if (policy == NULL || policy[0] == NULL) continue; @@ -155,8 +221,7 @@ _sysprof_instruments_prepare (GPtrArray *instruments, { SysprofInstrument *instrument = g_ptr_array_index (instruments, i); - g_ptr_array_add (futures, - SYSPROF_INSTRUMENT_GET_CLASS (instrument)->prepare (instrument, recording)); + g_ptr_array_add (futures, _sysprof_instrument_prepare (instrument, recording)); } if (futures->len == 0) @@ -182,8 +247,7 @@ _sysprof_instruments_record (GPtrArray *instruments, { SysprofInstrument *instrument = g_ptr_array_index (instruments, i); - g_ptr_array_add (futures, - SYSPROF_INSTRUMENT_GET_CLASS (instrument)->record (instrument, recording, cancellable)); + g_ptr_array_add (futures, _sysprof_instrument_record (instrument, recording, cancellable)); } if (futures->len == 0) @@ -208,11 +272,7 @@ _sysprof_instruments_process_started (GPtrArray *instruments, { SysprofInstrument *instrument = g_ptr_array_index (instruments, i); - if (SYSPROF_INSTRUMENT_GET_CLASS (instruments)->process_started == NULL) - continue; - - g_ptr_array_add (futures, - SYSPROF_INSTRUMENT_GET_CLASS (instrument)->process_started (instrument, recording, pid)); + g_ptr_array_add (futures, _sysprof_instrument_process_started (instrument, recording, pid)); } if (futures->len == 0) From e10ab3e32e70118cc2c2a6ea2882da0e59e4b3be Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 2 Jun 2023 12:59:01 -0700 Subject: [PATCH 0323/1030] libsysprof-profile: add augmentation phase to instruments This allows an instrument to augment the capture with additional information before the capture is closed/flushed to storage. --- .../sysprof-instrument-private.h | 4 ++++ src/libsysprof-profile/sysprof-instrument.c | 24 +++++++++++++++++++ src/libsysprof-profile/sysprof-recording.c | 11 ++++++++- 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/libsysprof-profile/sysprof-instrument-private.h b/src/libsysprof-profile/sysprof-instrument-private.h index f8a4d9df..9474ab9c 100644 --- a/src/libsysprof-profile/sysprof-instrument-private.h +++ b/src/libsysprof-profile/sysprof-instrument-private.h @@ -44,6 +44,8 @@ struct _SysprofInstrumentClass DexFuture *(*record) (SysprofInstrument *self, SysprofRecording *recording, GCancellable *cancellable); + DexFuture *(*augment) (SysprofInstrument *self, + SysprofRecording *recording); DexFuture *(*process_started) (SysprofInstrument *self, SysprofRecording *recording, int pid); @@ -56,6 +58,8 @@ DexFuture *_sysprof_instruments_prepare (GPtrArray *instruments, DexFuture *_sysprof_instruments_record (GPtrArray *instruments, SysprofRecording *recording, GCancellable *cancellable); +DexFuture *_sysprof_instruments_augment (GPtrArray *instruments, + SysprofRecording *recording); DexFuture *_sysprof_instruments_process_started (GPtrArray *instruments, SysprofRecording *recording, int pid); diff --git a/src/libsysprof-profile/sysprof-instrument.c b/src/libsysprof-profile/sysprof-instrument.c index 790efa94..9c0b33a1 100644 --- a/src/libsysprof-profile/sysprof-instrument.c +++ b/src/libsysprof-profile/sysprof-instrument.c @@ -256,6 +256,30 @@ _sysprof_instruments_record (GPtrArray *instruments, return dex_future_allv ((DexFuture **)futures->pdata, futures->len); } +DexFuture * +_sysprof_instruments_augment (GPtrArray *instruments, + SysprofRecording *recording) +{ + g_autoptr(GPtrArray) futures = NULL; + + g_return_val_if_fail (instruments != NULL, NULL); + g_return_val_if_fail (SYSPROF_IS_RECORDING (recording), NULL); + + futures = g_ptr_array_new_with_free_func (dex_unref); + + for (guint i = 0; i < instruments->len; i++) + { + SysprofInstrument *instrument = g_ptr_array_index (instruments, i); + + g_ptr_array_add (futures, _sysprof_instrument_augment (instrument, recording)); + } + + if (futures->len == 0) + return dex_future_new_for_boolean (TRUE); + + return dex_future_allv ((DexFuture **)futures->pdata, futures->len); +} + DexFuture * _sysprof_instruments_process_started (GPtrArray *instruments, SysprofRecording *recording, diff --git a/src/libsysprof-profile/sysprof-recording.c b/src/libsysprof-profile/sysprof-recording.c index bfbec64f..8ed6a816 100644 --- a/src/libsysprof-profile/sysprof-recording.c +++ b/src/libsysprof-profile/sysprof-recording.c @@ -76,6 +76,7 @@ sysprof_recording_fiber (gpointer user_data) g_autoptr(DexFuture) record = NULL; g_autoptr(GError) error = NULL; gint64 begin_time; + gint64 end_time; g_assert (SYSPROF_IS_RECORDING (self)); @@ -128,13 +129,21 @@ sysprof_recording_fiber (gpointer user_data) } stop_recording: + end_time = SYSPROF_CAPTURE_CURRENT_TIME; + /* Signal cancellable so that anything lingering has a chance to be * cleaned up, cascading into other subsystems. */ g_cancellable_cancel (cancellable); + /* Let instruments augment the capture. Some instruments may include + * extra information about the capture such as symbol names and their + * address ranges per-process. + */ + dex_await (_sysprof_instruments_augment (self->instruments, self), NULL); + /* Update start/end times to be the "running time" */ - _sysprof_capture_writer_set_time_range (self->writer, begin_time, SYSPROF_CAPTURE_CURRENT_TIME); + _sysprof_capture_writer_set_time_range (self->writer, begin_time, end_time); if (error != NULL) return dex_future_new_for_error (g_steal_pointer (&error)); From 728a9ce86a23f938872a96bf2fbee30513df2f77 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 2 Jun 2023 13:38:03 -0700 Subject: [PATCH 0324/1030] libsysprof-profiler: add scaffolding for disk usage This still needs porting from libsysprof, but this gets the scaffolding in place to bring over those counters. --- src/libsysprof-profile/meson.build | 4 +- src/libsysprof-profile/sysprof-disk-usage.c | 67 ++++++++++++++++++++ src/libsysprof-profile/sysprof-disk-usage.h | 42 ++++++++++++ src/libsysprof-profile/sysprof-profile.h | 1 + src/libsysprof-profile/tests/test-profiler.c | 1 + 5 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 src/libsysprof-profile/sysprof-disk-usage.c create mode 100644 src/libsysprof-profile/sysprof-disk-usage.h diff --git a/src/libsysprof-profile/meson.build b/src/libsysprof-profile/meson.build index 957a3509..2d3b3b6f 100644 --- a/src/libsysprof-profile/meson.build +++ b/src/libsysprof-profile/meson.build @@ -1,5 +1,6 @@ libsysprof_profile_public_sources = [ 'sysprof-cpu-usage.c', + 'sysprof-disk-usage.c', 'sysprof-instrument.c', 'sysprof-network-usage.c', 'sysprof-profiler.c', @@ -17,8 +18,9 @@ libsysprof_profile_private_sources = [ libsysprof_profile_public_headers = [ 'sysprof-profile.h', - 'sysprof-instrument.h', 'sysprof-cpu-usage.h', + 'sysprof-disk-usage.h', + 'sysprof-instrument.h', 'sysprof-network-usage.h', 'sysprof-profiler.h', 'sysprof-recording.h', diff --git a/src/libsysprof-profile/sysprof-disk-usage.c b/src/libsysprof-profile/sysprof-disk-usage.c new file mode 100644 index 00000000..689c27a8 --- /dev/null +++ b/src/libsysprof-profile/sysprof-disk-usage.c @@ -0,0 +1,67 @@ +/* sysprof-disk-usage.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-disk-usage.h" +#include "sysprof-instrument-private.h" + +struct _SysprofDiskUsage +{ + SysprofInstrument parent_instance; +}; + +struct _SysprofDiskUsageClass +{ + SysprofInstrumentClass parent_class; +}; + +G_DEFINE_FINAL_TYPE (SysprofDiskUsage, sysprof_disk_usage, SYSPROF_TYPE_INSTRUMENT) + +static DexFuture * +sysprof_disk_usage_record (SysprofInstrument *instrument, + SysprofRecording *recording, + GCancellable *cancellable) +{ + g_assert (SYSPROF_IS_INSTRUMENT (instrument)); + g_assert (SYSPROF_IS_RECORDING (recording)); + g_assert (G_IS_CANCELLABLE (cancellable)); + + return dex_future_new_for_boolean (TRUE); +} + +static void +sysprof_disk_usage_class_init (SysprofDiskUsageClass *klass) +{ + SysprofInstrumentClass *instrument_class = SYSPROF_INSTRUMENT_CLASS (klass); + + instrument_class->record = sysprof_disk_usage_record; +} + +static void +sysprof_disk_usage_init (SysprofDiskUsage *self) +{ +} + +SysprofInstrument * +sysprof_disk_usage_new (void) +{ + return g_object_new (SYSPROF_TYPE_DISK_USAGE, NULL); +} diff --git a/src/libsysprof-profile/sysprof-disk-usage.h b/src/libsysprof-profile/sysprof-disk-usage.h new file mode 100644 index 00000000..af6192e9 --- /dev/null +++ b/src/libsysprof-profile/sysprof-disk-usage.h @@ -0,0 +1,42 @@ +/* sysprof-disk-usage.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-instrument.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_DISK_USAGE (sysprof_disk_usage_get_type()) +#define SYSPROF_IS_DISK_USAGE(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_DISK_USAGE) +#define SYSPROF_DISK_USAGE(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_DISK_USAGE, SysprofDiskUsage) +#define SYSPROF_DISK_USAGE_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_DISK_USAGE, SysprofDiskUsageClass) + +typedef struct _SysprofDiskUsage SysprofDiskUsage; +typedef struct _SysprofDiskUsageClass SysprofDiskUsageClass; + +SYSPROF_AVAILABLE_IN_ALL +GType sysprof_disk_usage_get_type (void) G_GNUC_CONST; +SYSPROF_AVAILABLE_IN_ALL +SysprofInstrument *sysprof_disk_usage_new (void); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofDiskUsage, g_object_unref) + +G_END_DECLS diff --git a/src/libsysprof-profile/sysprof-profile.h b/src/libsysprof-profile/sysprof-profile.h index 74f6e8ea..e8aace6d 100644 --- a/src/libsysprof-profile/sysprof-profile.h +++ b/src/libsysprof-profile/sysprof-profile.h @@ -26,6 +26,7 @@ G_BEGIN_DECLS #define SYSPROF_PROFILE_INSIDE # include "sysprof-cpu-usage.h" +# include "sysprof-disk-usage.h" # include "sysprof-instrument.h" # include "sysprof-network-usage.h" # include "sysprof-profiler.h" diff --git a/src/libsysprof-profile/tests/test-profiler.c b/src/libsysprof-profile/tests/test-profiler.c index 8e7780a8..5c3aa800 100644 --- a/src/libsysprof-profile/tests/test-profiler.c +++ b/src/libsysprof-profile/tests/test-profiler.c @@ -114,6 +114,7 @@ main (int argc, profiler = sysprof_profiler_new (); sysprof_profiler_add_instrument (profiler, sysprof_cpu_usage_new ()); + sysprof_profiler_add_instrument (profiler, sysprof_disk_usage_new ()); sysprof_profiler_add_instrument (profiler, sysprof_network_usage_new ()); sysprof_profiler_record_async (profiler, writer, NULL, record_cb, NULL); From a256d19f6815c40fd632b323f99f72d0b8686889 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 2 Jun 2023 14:32:32 -0700 Subject: [PATCH 0325/1030] libsysprof-profiler: provide recording state to fiber --- src/libsysprof-profile/sysprof-disk-usage.c | 39 ++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/src/libsysprof-profile/sysprof-disk-usage.c b/src/libsysprof-profile/sysprof-disk-usage.c index 689c27a8..f0b2b0ea 100644 --- a/src/libsysprof-profile/sysprof-disk-usage.c +++ b/src/libsysprof-profile/sysprof-disk-usage.c @@ -35,16 +35,53 @@ struct _SysprofDiskUsageClass G_DEFINE_FINAL_TYPE (SysprofDiskUsage, sysprof_disk_usage, SYSPROF_TYPE_INSTRUMENT) +typedef struct _Record +{ + SysprofRecording *recording; + DexFuture *cancellable; +} Record; + +static void +record_free (gpointer data) +{ + Record *record = data; + + g_clear_object (&record->recording); + dex_clear (&record->cancellable); + g_free (record); +} + +static DexFuture * +sysprof_disk_usage_record_fiber (gpointer user_data) +{ + Record *record = user_data; + + g_assert (record != NULL); + g_assert (SYSPROF_IS_RECORDING (record->recording)); + g_assert (DEX_IS_CANCELLABLE (record->cancellable)); + + return dex_future_new_for_boolean (TRUE); +} + static DexFuture * sysprof_disk_usage_record (SysprofInstrument *instrument, SysprofRecording *recording, GCancellable *cancellable) { + Record *record; + g_assert (SYSPROF_IS_INSTRUMENT (instrument)); g_assert (SYSPROF_IS_RECORDING (recording)); g_assert (G_IS_CANCELLABLE (cancellable)); - return dex_future_new_for_boolean (TRUE); + record = g_new0 (Record, 1); + record->recording = g_object_ref (recording); + record->cancellable = dex_cancellable_new_from_cancellable (cancellable); + + return dex_scheduler_spawn (NULL, 0, + sysprof_disk_usage_record_fiber, + record, + record_free); } static void From 337ddb4c379e38bd986b1ecc3d906993231e6d6b Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 2 Jun 2023 14:42:33 -0700 Subject: [PATCH 0326/1030] libsysprof-profile: use new errno helpers --- src/libsysprof-profile/sysprof-network-usage.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/libsysprof-profile/sysprof-network-usage.c b/src/libsysprof-profile/sysprof-network-usage.c index 0db5a966..a1722203 100644 --- a/src/libsysprof-profile/sysprof-network-usage.c +++ b/src/libsysprof-profile/sysprof-network-usage.c @@ -131,9 +131,7 @@ sysprof_network_usage_record_fiber (gpointer user_data) writer = _sysprof_recording_writer (record->recording); if (-1 == (stat_fd = open ("/proc/net/dev", O_RDONLY|O_CLOEXEC))) - return dex_future_new_reject (G_IO_ERROR, - g_io_error_from_errno (errno), - "%s", g_strerror (errno)); + return dex_future_new_for_errno (errno); devices = g_array_new (FALSE, FALSE, sizeof (DeviceUsage)); @@ -162,9 +160,7 @@ sysprof_network_usage_record_fiber (gpointer user_data) n_read = dex_await_int64 (dex_aio_read (NULL, stat_fd, buf, sizeof buf, 0), &error); if (n_read <= 0) - return dex_future_new_reject (G_IO_ERROR, - g_io_error_from_errno (errno), - "%s", g_strerror (errno)); + return dex_future_new_for_errno (errno); lineno = 0; line_reader_init (&reader, buf, n_read); From 0146050618ef56122967cc1dfd0f26f308ce5da3 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 2 Jun 2023 15:42:38 -0700 Subject: [PATCH 0327/1030] libsysprof-profile: add SysprofDiskUsage This is essentially the SysprofDiskstatSource in libsysprof ported to the new instruments API. --- src/libsysprof-profile/sysprof-disk-usage.c | 335 ++++++++++++++++++++ 1 file changed, 335 insertions(+) diff --git a/src/libsysprof-profile/sysprof-disk-usage.c b/src/libsysprof-profile/sysprof-disk-usage.c index f0b2b0ea..918689f3 100644 --- a/src/libsysprof-profile/sysprof-disk-usage.c +++ b/src/libsysprof-profile/sysprof-disk-usage.c @@ -20,8 +20,17 @@ #include "config.h" +#include + +#include + #include "sysprof-disk-usage.h" #include "sysprof-instrument-private.h" +#include "sysprof-recording-private.h" + +#include "line-reader-private.h" + +#define ADD_FROM_CHAR(v, c) (((v)*10L)+((c)-'0')) struct _SysprofDiskUsage { @@ -33,12 +42,57 @@ struct _SysprofDiskUsageClass SysprofInstrumentClass parent_class; }; +typedef struct _DiskUsage +{ + /* Counter IDs */ + guint reads_total_id; + guint writes_total_id; + + /* Index where values are stored */ + guint values_at; + + char device[32]; + + gint64 reads_total; + gint64 reads_merged; + gint64 reads_sectors; + gint64 reads_msec; + gint64 writes_total; + gint64 writes_merged; + gint64 writes_sectors; + gint64 writes_msec; + gint64 iops_active; + gint64 iops_msec; + gint64 iops_msec_weighted; +} DiskUsage; + +typedef enum _Column +{ + /* -2 */ COLUMN_MAJOR, + /* -1 */ COLUMN_MINOR, + /* 0 */ COLUMN_NAME, + /* 1 */ COLUMN_READS_TOTAL, + /* 2 */ COLUMN_READS_MERGED, + /* 3 */ COLUMN_READS_SECTORS, + /* 4 */ COLUMN_READS_MSEC, + /* 5 */ COLUMN_WRITES_TOTAL, + /* 6 */ COLUMN_WRITES_MERGED, + /* 7 */ COLUMN_WRITES_SECTORS, + /* 8 */ COLUMN_WRITES_MSEC, + /* 9 */ COLUMN_IOPS_ACTIVE, + /* 10 */ COLUMN_IOPS_MSEC, + /* 11 */ COLUMN_IOPS_MSEC_WEIGHTED, +} Column; + G_DEFINE_FINAL_TYPE (SysprofDiskUsage, sysprof_disk_usage, SYSPROF_TYPE_INSTRUMENT) typedef struct _Record { SysprofRecording *recording; DexFuture *cancellable; + GArray *devices; + GArray *ids; + GArray *values; } Record; static void @@ -47,19 +101,297 @@ record_free (gpointer data) Record *record = data; g_clear_object (&record->recording); + g_clear_pointer (&record->devices, g_array_unref); + g_clear_pointer (&record->ids, g_array_unref); + g_clear_pointer (&record->values, g_array_unref); dex_clear (&record->cancellable); g_free (record); } +static DiskUsage * +register_counters_by_name (Record *record, + const char *name) +{ + static const SysprofCaptureCounterValue zeroval = {0}; + SysprofCaptureCounter ctr[2] = {0}; + SysprofCaptureWriter *writer; + DiskUsage ds = {0}; + + g_assert (record != NULL); + g_assert (name != NULL); + + writer = _sysprof_recording_writer (record->recording); + + ds.values_at = record->ids->len; + ds.reads_total_id = sysprof_capture_writer_request_counter (writer, 1); + ds.writes_total_id = sysprof_capture_writer_request_counter (writer, 1); + + g_strlcpy (ds.device, name, sizeof ds.device); + + g_strlcpy (ctr[0].category, "Disk", sizeof ctr[0].category); + g_snprintf (ctr[0].name, sizeof ctr[0].name, "Total Reads (%s)", name); + g_strlcpy (ctr[0].description, name, sizeof ctr[0].description); + ctr[0].id = ds.reads_total_id; + ctr[0].type = SYSPROF_CAPTURE_COUNTER_INT64; + ctr[0].value.v64 = 0; + + g_strlcpy (ctr[1].category, "Disk", sizeof ctr[1].category); + g_snprintf (ctr[1].name, sizeof ctr[1].name, "Total Writes (%s)", name); + g_strlcpy (ctr[1].description, name, sizeof ctr[1].description); + ctr[1].id = ds.writes_total_id; + ctr[1].type = SYSPROF_CAPTURE_COUNTER_INT64; + ctr[1].value.v64 = 0; + + sysprof_capture_writer_define_counters (writer, + SYSPROF_CAPTURE_CURRENT_TIME, + -1, + -1, + ctr, + G_N_ELEMENTS (ctr)); + + g_array_append_val (record->devices, ds); + g_array_append_val (record->ids, ds.reads_total_id); + g_array_append_val (record->ids, ds.writes_total_id); + g_array_append_val (record->values, zeroval); + g_array_append_val (record->values, zeroval); + + return &g_array_index (record->devices, DiskUsage, record->devices->len-1); +} + +static DiskUsage * +find_device_by_name (Record *record, + const char *name) +{ + g_assert (record != NULL); + g_assert (record->devices != NULL); + g_assert (name != NULL); + + for (guint i = 0; i < record->devices->len; i++) + { + DiskUsage *ds = &g_array_index (record->devices, DiskUsage, i); + + if (strcmp (name, ds->device) == 0) + return ds; + } + + return NULL; +} + static DexFuture * sysprof_disk_usage_record_fiber (gpointer user_data) { Record *record = user_data; + SysprofCaptureWriter *writer; + g_autofd int stat_fd = -1; + char buf[4096*4]; + LineReader reader; + DiskUsage *combined; + gint64 combined_reads_total = 0; + gint64 combined_writes_total = 0; + gboolean skip_publish = TRUE; g_assert (record != NULL); g_assert (SYSPROF_IS_RECORDING (record->recording)); g_assert (DEX_IS_CANCELLABLE (record->cancellable)); + if (-1 == (stat_fd = open ("/proc/diskstats", O_RDONLY|O_CLOEXEC))) + return dex_future_new_for_errno (errno); + + writer = _sysprof_recording_writer (record->recording); + + register_counters_by_name (record, "Combined"); + + for (;;) + { + g_autoptr(DexFuture) read_future = NULL; + gssize n_read; + char *line; + gsize line_len; + + /* Read a new copy of our diskstats or bail from our + * recording loop. If cancellation future rejects, then + * we also break out of our recording loop. + */ + read_future = dex_aio_read (NULL, stat_fd, buf, sizeof buf-1, 0); + if (!dex_await (dex_future_first (dex_ref (record->cancellable), + dex_ref (read_future), + NULL), + NULL)) + break; + + n_read = dex_await_int64 (dex_ref (read_future), NULL); + if (n_read < 0) + break; + + line_reader_init (&reader, buf, n_read); + while ((line = line_reader_next (&reader, &line_len))) + { + DiskUsage ds = {0}; + DiskUsage *found; + gint64 dummy = 0; + int column = COLUMN_MAJOR; + + line[line_len] = 0; + + /* Skip past initial space */ + while (g_ascii_isspace (*line)) + line++; + + for (const char *ptr = line; *ptr; ptr++) + { + char ch; + + /* Skip past space and advance to next column */ + if (g_ascii_isspace (*ptr)) + { + while (g_ascii_isspace (*ptr)) + ptr++; + column++; + } + + ch = *ptr; + + switch (column) + { + case COLUMN_MAJOR: + case COLUMN_MINOR: + default: + dummy = ADD_FROM_CHAR (dummy, ch); + break; + + case COLUMN_NAME: + { + guint j; + + for (j = 0; j < sizeof ds.device && ds.device[j] != 0; j++) { /* Do Nothing */ } + if (j < sizeof ds.device) + ds.device[j] = ch; + ds.device[sizeof ds.device - 1] = 0; + + break; + } + + case COLUMN_READS_TOTAL: + ds.reads_total = ADD_FROM_CHAR (ds.reads_total, ch); + break; + + case COLUMN_READS_MERGED: + ds.reads_merged = ADD_FROM_CHAR (ds.reads_merged, ch); + break; + + case COLUMN_READS_SECTORS: + ds.reads_sectors = ADD_FROM_CHAR (ds.reads_sectors, ch); + break; + + case COLUMN_READS_MSEC: + ds.reads_msec = ADD_FROM_CHAR (ds.reads_msec, ch); + break; + + case COLUMN_WRITES_TOTAL: + ds.writes_total = ADD_FROM_CHAR (ds.writes_total, ch); + break; + + case COLUMN_WRITES_MERGED: + ds.writes_merged = ADD_FROM_CHAR (ds.writes_merged, ch); + break; + + case COLUMN_WRITES_SECTORS: + ds.writes_sectors = ADD_FROM_CHAR (ds.writes_sectors, ch); + break; + + case COLUMN_WRITES_MSEC: + ds.writes_msec = ADD_FROM_CHAR (ds.writes_msec, ch); + break; + + case COLUMN_IOPS_ACTIVE: + ds.iops_active = ADD_FROM_CHAR (ds.iops_active, ch); + break; + + case COLUMN_IOPS_MSEC: + ds.iops_msec = ADD_FROM_CHAR (ds.iops_msec, ch); + break; + + case COLUMN_IOPS_MSEC_WEIGHTED: + ds.iops_msec_weighted = ADD_FROM_CHAR (ds.iops_msec_weighted, ch); + break; + } + } + + g_strstrip (ds.device); + + if (ds.device[0]) + { + guint p; + gint64 reads_total; + gint64 writes_total; + + if (!(found = find_device_by_name (record, ds.device))) + found = register_counters_by_name (record, ds.device); + + /* Calculate new value, based on diff from previous */ + reads_total = ds.reads_total - found->reads_total; + writes_total = ds.writes_total - found->writes_total; + + /* Update value for publishing */ + p = found->values_at; + g_array_index (record->values, SysprofCaptureCounterValue, p).v64 = reads_total; + g_array_index (record->values, SysprofCaptureCounterValue, p+1).v64 = writes_total; + + /* Update combined values */ + combined_reads_total += reads_total; + combined_writes_total += writes_total; + + /* Save current value for diff on next poll */ + found->reads_total = ds.reads_total; + found->writes_total = ds.writes_total; + } + } + + if ((combined = find_device_by_name (record, "Combined"))) + { + /* TODO: It would be nice to not double count disk ops multiple + * times based on the parition, etc. + * + * For example: nvme0n1 nvme0n1p1 nvme0n1p2 nvme0n1p3 may get + * accounted multiple times even though they are all nvme0n1. + * + * The other option, is to just not do "Combined" counters. + */ + + g_array_index (record->values, + SysprofCaptureCounterValue, + combined->values_at).v64 + = combined_reads_total - combined->reads_total; + g_array_index (record->values, + SysprofCaptureCounterValue, + combined->values_at+1).v64 + = combined_writes_total - combined->writes_total; + + combined->reads_total = combined_reads_total; + combined->writes_total = combined_writes_total; + } + + g_assert (record->ids->len == record->values->len); + + if (skip_publish) + skip_publish = FALSE; + else + sysprof_capture_writer_set_counters (writer, + SYSPROF_CAPTURE_CURRENT_TIME, + -1, + -1, + (const guint *)(gpointer)record->ids->data, + (const SysprofCaptureCounterValue *)(gpointer)record->values->data, + record->ids->len); + + dex_await (dex_future_first (dex_ref (record->cancellable), + dex_timeout_new_usec (G_USEC_PER_SEC / 2), + NULL), + NULL); + if (dex_future_get_status (record->cancellable) != DEX_FUTURE_STATUS_PENDING) + break; + } + return dex_future_new_for_boolean (TRUE); } @@ -77,6 +409,9 @@ sysprof_disk_usage_record (SysprofInstrument *instrument, record = g_new0 (Record, 1); record->recording = g_object_ref (recording); record->cancellable = dex_cancellable_new_from_cancellable (cancellable); + record->devices = g_array_new (FALSE, FALSE, sizeof (DiskUsage)); + record->ids = g_array_new (FALSE, FALSE, sizeof (guint)); + record->values = g_array_new (FALSE, FALSE, sizeof (SysprofCaptureCounterValue)); return dex_scheduler_spawn (NULL, 0, sysprof_disk_usage_record_fiber, From ba9d29fa337247085ebbef92421aa7c5e39c1893 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 2 Jun 2023 15:57:46 -0700 Subject: [PATCH 0328/1030] libsysprof-profile: add trace_fd helper for spawnable I'd like to stop using tracefd, but since we need to maintain compatability with older tooling, lets at least make it more ergonomic than adding an instrument(s) for it. --- src/libsysprof-profile/sysprof-spawnable.c | 50 ++++++++++++++++++++++ src/libsysprof-profile/sysprof-spawnable.h | 5 ++- 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/src/libsysprof-profile/sysprof-spawnable.c b/src/libsysprof-profile/sysprof-spawnable.c index efe670ce..631c82ad 100644 --- a/src/libsysprof-profile/sysprof-spawnable.c +++ b/src/libsysprof-profile/sysprof-spawnable.c @@ -22,6 +22,8 @@ #include +#include + #include "sysprof-spawnable.h" typedef struct @@ -328,3 +330,51 @@ sysprof_spawnable_set_cwd (SysprofSpawnable *self, self->cwd = g_strdup (cwd); } } + +/** + * sysprof_spawnable_add_trace_fd: + * @self: a #SysprofSpawnable + * @envvar: (nullable): the environment variable + * + * Adds an environment variable to the spawnable that will contain a + * "tracing file-descriptor". The spawned process can use + * `sysprof_capture_writer_new_from_env()` if @envvar is %NULL + * or with `getenv()` and `sysprof_capture_writer_new_from_fd()`. + * + * If @envvar is %NULL, "SYSPROF_TRACE_FD" will be used. + * + * The caller is responsible for closin the resulting FD. + * + * Returns: A file-descriptor which can be used to read the trace or + * -1 upon failure and `errno` is set. Caller must `close()` the + * FD if >= 0. + */ +int +sysprof_spawnable_add_trace_fd (SysprofSpawnable *self, + const char *envvar) +{ + g_autofd int fd = -1; + g_autofd int dest = -1; + g_autofree char *name = NULL; + g_autofree char *fdstr = NULL; + + g_return_val_if_fail (SYSPROF_IS_SPAWNABLE (self), -1); + + if (envvar == NULL) + envvar = "SYSPROF_TRACE_FD"; + + name = g_strdup_printf ("[sysprof-tracefd:%s]", envvar); + + if (-1 == (fd = sysprof_memfd_create (name))) + return -1; + + if (-1 == (dest = dup (fd))) + return -1; + + fdstr = g_strdup_printf ("%d", dest); + + sysprof_spawnable_setenv (self, envvar, fdstr); + sysprof_spawnable_take_fd (self, g_steal_fd (&dest), -1); + + return g_steal_fd (&fd); +} diff --git a/src/libsysprof-profile/sysprof-spawnable.h b/src/libsysprof-profile/sysprof-spawnable.h index f8ad8bf7..81814e48 100644 --- a/src/libsysprof-profile/sysprof-spawnable.h +++ b/src/libsysprof-profile/sysprof-spawnable.h @@ -22,7 +22,7 @@ #include -#include "sysprof-version-macros.h" +#include G_BEGIN_DECLS @@ -75,6 +75,9 @@ SYSPROF_AVAILABLE_IN_ALL void sysprof_spawnable_set_starting_fd (SysprofSpawnable *self, gint starting_fd); SYSPROF_AVAILABLE_IN_ALL +int sysprof_spawnable_add_trace_fd (SysprofSpawnable *self, + const char *envvar); +SYSPROF_AVAILABLE_IN_ALL GSubprocess *sysprof_spawnable_spawn (SysprofSpawnable *self, GError **error); SYSPROF_AVAILABLE_IN_ALL From 5746add9c65d7049bebb72dcc1d97ce33eb25091 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 2 Jun 2023 16:15:27 -0700 Subject: [PATCH 0329/1030] libsysprof-profile: give Profiler a spawnable And pass it along to the recording so that it can manage spawning the subprocess during the record loop. --- src/libsysprof-profile/sysprof-profiler.c | 60 ++++++++++++++++++- src/libsysprof-profile/sysprof-profiler.h | 6 ++ .../sysprof-recording-private.h | 1 + src/libsysprof-profile/sysprof-recording.c | 32 ++-------- 4 files changed, 69 insertions(+), 30 deletions(-) diff --git a/src/libsysprof-profile/sysprof-profiler.c b/src/libsysprof-profile/sysprof-profiler.c index 0a93432d..ab3c133e 100644 --- a/src/libsysprof-profile/sysprof-profiler.c +++ b/src/libsysprof-profile/sysprof-profiler.c @@ -30,20 +30,29 @@ struct _SysprofProfiler { - GObject parent_instance; - GPtrArray *instruments; + GObject parent_instance; + GPtrArray *instruments; + SysprofSpawnable *spawnable; }; enum { PROP_0, + PROP_SPAWNABLE, N_PROPS }; +static GParamSpec *properties [N_PROPS]; + G_DEFINE_FINAL_TYPE (SysprofProfiler, sysprof_profiler, G_TYPE_OBJECT) static void sysprof_profiler_finalize (GObject *object) { + SysprofProfiler *self = (SysprofProfiler *)object; + + g_clear_pointer (&self->instruments, g_ptr_array_unref); + g_clear_object (&self->spawnable); + G_OBJECT_CLASS (sysprof_profiler_parent_class)->finalize (object); } @@ -53,8 +62,14 @@ sysprof_profiler_get_property (GObject *object, GValue *value, GParamSpec *pspec) { + SysprofProfiler *self = SYSPROF_PROFILER (object); + switch (prop_id) { + case PROP_SPAWNABLE: + g_value_set_object (value, sysprof_profiler_get_spawnable (self)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } @@ -66,8 +81,14 @@ sysprof_profiler_set_property (GObject *object, const GValue *value, GParamSpec *pspec) { + SysprofProfiler *self = SYSPROF_PROFILER (object); + switch (prop_id) { + case PROP_SPAWNABLE: + sysprof_profiler_set_spawnable (self, g_value_get_object (value)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } @@ -81,6 +102,13 @@ sysprof_profiler_class_init (SysprofProfilerClass *klass) object_class->finalize = sysprof_profiler_finalize; object_class->get_property = sysprof_profiler_get_property; object_class->set_property = sysprof_profiler_set_property; + + properties [PROP_SPAWNABLE] = + g_param_spec_object ("spawnable", NULL, NULL, + SYSPROF_TYPE_SPAWNABLE, + (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); } static void @@ -139,6 +167,7 @@ sysprof_profiler_record_async (SysprofProfiler *self, g_task_set_source_tag (task, sysprof_profiler_record_async); recording = _sysprof_recording_new (writer, + self->spawnable, (SysprofInstrument **)self->instruments->pdata, self->instruments->len); @@ -169,3 +198,30 @@ sysprof_profiler_record_finish (SysprofProfiler *self, return g_task_propagate_pointer (G_TASK (result), error); } + +/** + * sysprof_profiler_get_spawnable: + * @self: a #SysprofProfiler + * + * Gets the #SysprofProfiler:spawnable property. + * + * Returns: (nullable) (transfer none): a #SysprofSpawnable or %NULL + */ +SysprofSpawnable * +sysprof_profiler_get_spawnable (SysprofProfiler *self) +{ + g_return_val_if_fail (SYSPROF_IS_PROFILER (self), NULL); + + return self->spawnable; +} + +void +sysprof_profiler_set_spawnable (SysprofProfiler *self, + SysprofSpawnable *spawnable) +{ + g_return_if_fail (SYSPROF_IS_PROFILER (self)); + g_return_if_fail (!spawnable || SYSPROF_IS_SPAWNABLE (spawnable)); + + if (g_set_object (&self->spawnable, spawnable)) + g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_SPAWNABLE]); +} diff --git a/src/libsysprof-profile/sysprof-profiler.h b/src/libsysprof-profile/sysprof-profiler.h index a8be07f5..42fec019 100644 --- a/src/libsysprof-profile/sysprof-profiler.h +++ b/src/libsysprof-profile/sysprof-profiler.h @@ -26,6 +26,7 @@ #include "sysprof-instrument.h" #include "sysprof-recording.h" +#include "sysprof-spawnable.h" G_BEGIN_DECLS @@ -37,6 +38,11 @@ G_DECLARE_FINAL_TYPE (SysprofProfiler, sysprof_profiler, SYSPROF, PROFILER, GObj SYSPROF_AVAILABLE_IN_ALL SysprofProfiler *sysprof_profiler_new (void); SYSPROF_AVAILABLE_IN_ALL +SysprofSpawnable *sysprof_profiler_get_spawnable (SysprofProfiler *self); +SYSPROF_AVAILABLE_IN_ALL +void sysprof_profiler_set_spawnable (SysprofProfiler *self, + SysprofSpawnable *spawnable); +SYSPROF_AVAILABLE_IN_ALL void sysprof_profiler_add_instrument (SysprofProfiler *self, SysprofInstrument *instrument); SYSPROF_AVAILABLE_IN_ALL diff --git a/src/libsysprof-profile/sysprof-recording-private.h b/src/libsysprof-profile/sysprof-recording-private.h index 9e553ec0..88a54626 100644 --- a/src/libsysprof-profile/sysprof-recording-private.h +++ b/src/libsysprof-profile/sysprof-recording-private.h @@ -29,6 +29,7 @@ G_BEGIN_DECLS SysprofRecording *_sysprof_recording_new (SysprofCaptureWriter *writer, + SysprofSpawnable *spawnable, SysprofInstrument **instruments, guint n_instruments); void _sysprof_recording_start (SysprofRecording *self); diff --git a/src/libsysprof-profile/sysprof-recording.c b/src/libsysprof-profile/sysprof-recording.c index 8ed6a816..1cfc94c7 100644 --- a/src/libsysprof-profile/sysprof-recording.c +++ b/src/libsysprof-profile/sysprof-recording.c @@ -164,45 +164,18 @@ sysprof_recording_finalize (GObject *object) g_clear_pointer (&self->writer, sysprof_capture_writer_unref); g_clear_pointer (&self->instruments, g_ptr_array_unref); + g_clear_object (&self->spawnable); dex_clear (&self->fiber); G_OBJECT_CLASS (sysprof_recording_parent_class)->finalize (object); } -static void -sysprof_recording_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - switch (prop_id) - { - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_recording_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - switch (prop_id) - { - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - static void sysprof_recording_class_init (SysprofRecordingClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = sysprof_recording_finalize; - object_class->get_property = sysprof_recording_get_property; - object_class->set_property = sysprof_recording_set_property; } static void @@ -214,6 +187,7 @@ sysprof_recording_init (SysprofRecording *self) SysprofRecording * _sysprof_recording_new (SysprofCaptureWriter *writer, + SysprofSpawnable *spawnable, SysprofInstrument **instruments, guint n_instruments) { @@ -224,6 +198,8 @@ _sysprof_recording_new (SysprofCaptureWriter *writer, self = g_object_new (SYSPROF_TYPE_RECORDING, NULL); self->writer = sysprof_capture_writer_ref (writer); + g_set_object (&self->spawnable, spawnable); + for (guint i = 0; i < n_instruments; i++) g_ptr_array_add (self->instruments, g_object_ref (instruments[i])); From 390e764aa7ee2afdab818b2e96a2ed36544d038b Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 2 Jun 2023 16:27:41 -0700 Subject: [PATCH 0330/1030] libsysprof-profile: spawn spawnable and escape upon exit We can use the new infinite future here to mimic running forever when there is no spawnable. Otherwise, if the spawnable exits we want to break out of the recording loop. --- src/libsysprof-profile/sysprof-recording.c | 30 ++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/src/libsysprof-profile/sysprof-recording.c b/src/libsysprof-profile/sysprof-recording.c index 1cfc94c7..25e1d35c 100644 --- a/src/libsysprof-profile/sysprof-recording.c +++ b/src/libsysprof-profile/sysprof-recording.c @@ -68,12 +68,27 @@ enum { G_DEFINE_FINAL_TYPE (SysprofRecording, sysprof_recording, G_TYPE_OBJECT) +static DexFuture * +_sysprof_recording_spawn (SysprofSpawnable *spawnable) +{ + g_autoptr(GSubprocess) subprocess = NULL; + g_autoptr(GError) error = NULL; + + g_assert (SYSPROF_IS_SPAWNABLE (spawnable)); + + if (!(subprocess = sysprof_spawnable_spawn (spawnable, &error))) + return dex_future_new_for_error (g_steal_pointer (&error)); + + return dex_subprocess_wait_check (subprocess); +} + static DexFuture * sysprof_recording_fiber (gpointer user_data) { SysprofRecording *self = user_data; g_autoptr(GCancellable) cancellable = NULL; g_autoptr(DexFuture) record = NULL; + g_autoptr(DexFuture) monitor = NULL; g_autoptr(GError) error = NULL; gint64 begin_time; gint64 end_time; @@ -99,6 +114,12 @@ sysprof_recording_fiber (gpointer user_data) /* Now take our begin time now that all instruments are notified */ begin_time = SYSPROF_CAPTURE_CURRENT_TIME; + /* If we need to spawn a subprocess, do it now */ + if (self->spawnable != NULL) + monitor = _sysprof_recording_spawn (self->spawnable); + else + monitor = dex_future_new_infinite (); + /* Wait for messages on our channel or the recording to complete */ for (;;) { @@ -107,11 +128,16 @@ sysprof_recording_fiber (gpointer user_data) /* Wait for either recording of all instruments to complete or a * message from our channel with what to do next. */ - if (!dex_await (dex_future_any (dex_ref (record), dex_ref (message), NULL), &error)) + if (!dex_await (dex_future_first (dex_ref (record), + dex_ref (message), + dex_ref (monitor), + NULL), + &error)) goto stop_recording; /* If record is not pending, then everything resolved/rejected */ - if (dex_future_get_status (record) != DEX_FUTURE_STATUS_PENDING) + if (dex_future_get_status (record) != DEX_FUTURE_STATUS_PENDING || + dex_future_get_status (monitor) != DEX_FUTURE_STATUS_PENDING) goto stop_recording; /* If message resolved, then we got a command to process */ From ff490eb1fdba452bae0462e433f019574552cf86 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 2 Jun 2023 16:49:56 -0700 Subject: [PATCH 0331/1030] libsysprof-profile: handle GIOStream properly --- src/libsysprof-profile/sysprof-controlfd-instrument.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/libsysprof-profile/sysprof-controlfd-instrument.c b/src/libsysprof-profile/sysprof-controlfd-instrument.c index 5280bf24..853d3488 100644 --- a/src/libsysprof-profile/sysprof-controlfd-instrument.c +++ b/src/libsysprof-profile/sysprof-controlfd-instrument.c @@ -141,7 +141,7 @@ finish: typedef struct _SysprofControlfdRecording { - GInputStream *stream; + GIOStream *stream; SysprofRecording *recording; DexFuture *cancellable; GArray *source_ids; @@ -189,15 +189,18 @@ sysprof_controlfd_instrument_record_fiber (gpointer user_data) { SysprofControlfdRecording *state = user_data; g_autoptr(GError) error = NULL; + GInputStream *input; g_assert (state != NULL); g_assert (SYSPROF_IS_RECORDING (state->recording)); g_assert (DEX_IS_CANCELLABLE (state->cancellable)); g_assert (state->source_ids != NULL); + input = g_io_stream_get_input_stream (G_IO_STREAM (state->stream)); + for (;;) { - g_autoptr(DexFuture) future = dex_input_stream_read_bytes (state->stream, 10, 0); + g_autoptr(DexFuture) future = dex_input_stream_read_bytes (input, 10, 0); g_autoptr(MappedRingBuffer) ring_buffer = NULL; g_autoptr(GBytes) bytes = NULL; const guint8 *data; @@ -278,6 +281,7 @@ sysprof_controlfd_instrument_record (SysprofInstrument *instrument, state = g_new0 (SysprofControlfdRecording, 1); state->recording = g_object_ref (recording); + state->stream = g_object_ref (G_IO_STREAM (self->connection)); state->cancellable = dex_cancellable_new_from_cancellable (cancellable); state->source_ids = g_array_new (FALSE, FALSE, sizeof (guint)); g_array_set_clear_func (state->source_ids, _g_clear_source); From 02b1d571cc9f2db3c9fbda55838a74b284689a1f Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 2 Jun 2023 16:50:08 -0700 Subject: [PATCH 0332/1030] libsysprof-profile: use char in sysprof-spawnable.h --- src/libsysprof-profile/sysprof-spawnable.h | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/libsysprof-profile/sysprof-spawnable.h b/src/libsysprof-profile/sysprof-spawnable.h index 81814e48..5aca47a6 100644 --- a/src/libsysprof-profile/sysprof-spawnable.h +++ b/src/libsysprof-profile/sysprof-spawnable.h @@ -39,30 +39,30 @@ SYSPROF_AVAILABLE_IN_ALL SysprofSpawnable *sysprof_spawnable_new (void); SYSPROF_AVAILABLE_IN_ALL void sysprof_spawnable_prepend_argv (SysprofSpawnable *self, - const gchar *argv); + const char *argv); SYSPROF_AVAILABLE_IN_ALL void sysprof_spawnable_append_argv (SysprofSpawnable *self, - const gchar *argv); + const char *argv); SYSPROF_AVAILABLE_IN_ALL void sysprof_spawnable_append_args (SysprofSpawnable *self, - const gchar * const *argv); + const char * const *argv); SYSPROF_AVAILABLE_IN_ALL void sysprof_spawnable_set_cwd (SysprofSpawnable *self, - const gchar *cwd); + const char *cwd); SYSPROF_AVAILABLE_IN_ALL -const gchar * const *sysprof_spawnable_get_argv (SysprofSpawnable *self); +const char * const *sysprof_spawnable_get_argv (SysprofSpawnable *self); SYSPROF_AVAILABLE_IN_ALL -const gchar * const *sysprof_spawnable_get_environ (SysprofSpawnable *self); +const char * const *sysprof_spawnable_get_environ (SysprofSpawnable *self); SYSPROF_AVAILABLE_IN_ALL void sysprof_spawnable_set_environ (SysprofSpawnable *self, - const gchar * const *environ); + const char * const *environ); SYSPROF_AVAILABLE_IN_ALL void sysprof_spawnable_setenv (SysprofSpawnable *self, - const gchar *key, - const gchar *value); + const char *key, + const char *value); SYSPROF_AVAILABLE_IN_ALL -const gchar *sysprof_spawnable_getenv (SysprofSpawnable *self, - const gchar *key); +const char *sysprof_spawnable_getenv (SysprofSpawnable *self, + const char *key); SYSPROF_AVAILABLE_IN_ALL gint sysprof_spawnable_take_fd (SysprofSpawnable *self, gint fd, From 1a3facf6ffa0492c8038ea21744c69b869ce90ae Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 2 Jun 2023 16:50:41 -0700 Subject: [PATCH 0333/1030] libsysprof-profiler: allow specifying test-profiler argv You can `./test-profiler -- ls -lsah` to profile a subprocess and have the profiler exit when the subprocess exits. --- src/libsysprof-profile/tests/test-profiler.c | 23 ++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/libsysprof-profile/tests/test-profiler.c b/src/libsysprof-profile/tests/test-profiler.c index 5c3aa800..e451cc38 100644 --- a/src/libsysprof-profile/tests/test-profiler.c +++ b/src/libsysprof-profile/tests/test-profiler.c @@ -19,6 +19,7 @@ */ #include +#include #include @@ -95,6 +96,8 @@ main (int argc, g_autoptr(SysprofProfiler) profiler = NULL; g_autoptr(GError) error = NULL; SysprofCaptureWriter *writer = NULL; + SysprofCaptureReader *reader = NULL; + g_autofd int trace_fd = -1; main_loop = g_main_loop_new (NULL, FALSE); @@ -117,6 +120,19 @@ main (int argc, sysprof_profiler_add_instrument (profiler, sysprof_disk_usage_new ()); sysprof_profiler_add_instrument (profiler, sysprof_network_usage_new ()); + for (int i = 1; i < argc; i++) + { + if (strcmp (argv[i], "--") == 0 && i+1 < argc) + { + g_autoptr(SysprofSpawnable) spawnable = sysprof_spawnable_new (); + + sysprof_spawnable_append_args (spawnable, (const char * const *)&argv[i+1]); + sysprof_profiler_set_spawnable (profiler, spawnable); + + trace_fd = sysprof_spawnable_add_trace_fd (spawnable, NULL); + } + } + sysprof_profiler_record_async (profiler, writer, NULL, record_cb, NULL); g_unix_signal_add (SIGINT, sigint_handler, main_loop); @@ -124,6 +140,13 @@ main (int argc, g_main_loop_run (main_loop); + if (trace_fd != -1) + { + if ((reader = sysprof_capture_reader_new_from_fd (g_steal_fd (&trace_fd)))) + sysprof_capture_writer_cat (writer, reader); + } + + g_clear_pointer (&reader, sysprof_capture_reader_unref); g_clear_pointer (&writer, sysprof_capture_writer_unref); return 0; From 37ad7041867352eec0d181de18f910492585647c Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 2 Jun 2023 17:09:44 -0700 Subject: [PATCH 0334/1030] libsysprof-profile: add ld_preload helper This is to be used by instruments to adjust the spawnable to inject preload into the spawned environment. --- src/libsysprof-profile/sysprof-spawnable.c | 16 ++++++++++++++++ src/libsysprof-profile/sysprof-spawnable.h | 3 +++ 2 files changed, 19 insertions(+) diff --git a/src/libsysprof-profile/sysprof-spawnable.c b/src/libsysprof-profile/sysprof-spawnable.c index 631c82ad..733b0080 100644 --- a/src/libsysprof-profile/sysprof-spawnable.c +++ b/src/libsysprof-profile/sysprof-spawnable.c @@ -378,3 +378,19 @@ sysprof_spawnable_add_trace_fd (SysprofSpawnable *self, return g_steal_fd (&fd); } + +void +sysprof_spawnable_add_ld_preload (SysprofSpawnable *self, + const char *library_path) +{ + g_autofree char *amended = NULL; + const char *val; + + g_return_if_fail (SYSPROF_IS_SPAWNABLE (self)); + g_return_if_fail (library_path != NULL); + + if ((val = sysprof_spawnable_getenv (self, "LD_PRELOAD"))) + library_path = amended = g_strdup_printf ("%s:%s", val, library_path); + + sysprof_spawnable_setenv (self, "LD_PRELOAD", library_path); +} diff --git a/src/libsysprof-profile/sysprof-spawnable.h b/src/libsysprof-profile/sysprof-spawnable.h index 5aca47a6..9f14b560 100644 --- a/src/libsysprof-profile/sysprof-spawnable.h +++ b/src/libsysprof-profile/sysprof-spawnable.h @@ -78,6 +78,9 @@ SYSPROF_AVAILABLE_IN_ALL int sysprof_spawnable_add_trace_fd (SysprofSpawnable *self, const char *envvar); SYSPROF_AVAILABLE_IN_ALL +void sysprof_spawnable_add_ld_preload (SysprofSpawnable *self, + const char *library_path); +SYSPROF_AVAILABLE_IN_ALL GSubprocess *sysprof_spawnable_spawn (SysprofSpawnable *self, GError **error); SYSPROF_AVAILABLE_IN_ALL From 0288e937e4745e4cfeab438a60772b65571cb90a Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 2 Jun 2023 18:01:18 -0700 Subject: [PATCH 0335/1030] libsysprof-profile: add SysprofMemoryUsage Ports sysprof-memory-source.c to the new SysprofInstrument API. --- src/libsysprof-profile/meson.build | 2 + src/libsysprof-profile/sysprof-memory-usage.c | 302 ++++++++++++++++++ src/libsysprof-profile/sysprof-memory-usage.h | 42 +++ src/libsysprof-profile/sysprof-profile.h | 1 + src/libsysprof-profile/tests/test-profiler.c | 1 + 5 files changed, 348 insertions(+) create mode 100644 src/libsysprof-profile/sysprof-memory-usage.c create mode 100644 src/libsysprof-profile/sysprof-memory-usage.h diff --git a/src/libsysprof-profile/meson.build b/src/libsysprof-profile/meson.build index 2d3b3b6f..9969b9ee 100644 --- a/src/libsysprof-profile/meson.build +++ b/src/libsysprof-profile/meson.build @@ -2,6 +2,7 @@ libsysprof_profile_public_sources = [ 'sysprof-cpu-usage.c', 'sysprof-disk-usage.c', 'sysprof-instrument.c', + 'sysprof-memory-usage.c', 'sysprof-network-usage.c', 'sysprof-profiler.c', 'sysprof-recording.c', @@ -21,6 +22,7 @@ libsysprof_profile_public_headers = [ 'sysprof-cpu-usage.h', 'sysprof-disk-usage.h', 'sysprof-instrument.h', + 'sysprof-memory-usage.h', 'sysprof-network-usage.h', 'sysprof-profiler.h', 'sysprof-recording.h', diff --git a/src/libsysprof-profile/sysprof-memory-usage.c b/src/libsysprof-profile/sysprof-memory-usage.c new file mode 100644 index 00000000..ee543025 --- /dev/null +++ b/src/libsysprof-profile/sysprof-memory-usage.c @@ -0,0 +1,302 @@ +/* sysprof-memory-usage.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "line-reader-private.h" + +#include "sysprof-memory-usage.h" +#include "sysprof-instrument-private.h" +#include "sysprof-recording-private.h" + +struct _SysprofMemoryUsage +{ + SysprofInstrument parent_instance; +}; + +struct _SysprofMemoryUsageClass +{ + SysprofInstrumentClass parent_class; +}; + +typedef struct _MemStat +{ + int stat_fd; + + union { + struct { + SysprofCaptureCounterValue used; + gint64 total; + gint64 avail; + gint64 free; + } sys; + }; +} MemStat; + +G_DEFINE_FINAL_TYPE (SysprofMemoryUsage, sysprof_memory_usage, SYSPROF_TYPE_INSTRUMENT) + +static GHashTable *keys; + +static gboolean +mem_stat_open (MemStat *st, + GError **error) +{ + memset (st, 0, sizeof *st); + + st->stat_fd = open ("/proc/meminfo", O_RDONLY|O_CLOEXEC); + + if (st->stat_fd == -1) + { + int errsv = errno; + g_set_error_literal (error, + G_IO_ERROR, + g_io_error_from_errno (errsv), + g_strerror (errsv)); + return FALSE; + } + + return TRUE; +} + +static gboolean +mem_stat_close (MemStat *st, + GError **error) +{ + g_assert (st != NULL); + + return g_clear_fd (&st->stat_fd, error); +} + +static void +mem_stat_parse (MemStat *st, + char *buf) +{ + char *bufptr = buf; + char *save = NULL; + + g_assert (st != NULL); + g_assert (buf != NULL); + + for (;;) + { + goffset off; + char *key; + char *value; + char *unit; + gint64 v64; + gint64 *v64ptr; + + /* Get the data key name */ + if (!(key = strtok_r (bufptr, " \n\t:", &save))) + break; + + bufptr = NULL; + + /* Offset from self to save value. Stop after getting to + * last value we care about. + */ + if (!(off = GPOINTER_TO_UINT (g_hash_table_lookup (keys, key)))) + break; + + /* Get the data value */ + if (!(value = strtok_r (bufptr, " \n\t:", &save))) + break; + + /* Parse the numeric value of this column */ + v64 = g_ascii_strtoll (value, NULL, 10); + if ((v64 == G_MININT64 || v64 == G_MAXINT64) && errno == ERANGE) + break; + + /* Get the data unit */ + unit = strtok_r (bufptr, " \n\t:", &save); + + if (g_strcmp0 (unit, "kB") == 0) + v64 *= 1024; + else if (g_strcmp0 (unit, "mB") == 0) + v64 *= 1024 * 1024; + + v64ptr = (gint64 *)(gpointer)(((gchar *)st) + off); + + *v64ptr = v64; + } + + /* Create pre-compiled value for used to simplify display */ + st->sys.used.vdbl = (gdouble)st->sys.total - (gdouble)st->sys.avail; +} + +typedef struct _Record +{ + SysprofRecording *recording; + DexFuture *cancellable; +} Record; + +static void +record_free (gpointer data) +{ + Record *record = data; + + g_clear_object (&record->recording); + dex_clear (&record->cancellable); + g_free (record); +} + +static DexFuture * +sysprof_memory_usage_record_fiber (gpointer user_data) +{ + Record *record = user_data; + SysprofCaptureWriter *writer; + g_autoptr(GError) error = NULL; + SysprofCaptureCounter counters[1]; + MemStat st; + char buf[4096]; + guint counter_id; + + g_assert (record != NULL); + g_assert (SYSPROF_IS_RECORDING (record->recording)); + g_assert (DEX_IS_FUTURE (record->cancellable)); + + writer = _sysprof_recording_writer (record->recording); + + if (!mem_stat_open (&st, &error)) + return dex_future_new_for_error (g_steal_pointer (&error)); + + counter_id = sysprof_capture_writer_request_counter (writer, 1); + + g_strlcpy (counters[0].category, "Memory", sizeof counters[0].category); + g_strlcpy (counters[0].name, "Used", sizeof counters[0].name); + g_strlcpy (counters[0].description, "Memory used by system", sizeof counters[0].description); + + counters[0].id = counter_id; + counters[0].type = SYSPROF_CAPTURE_COUNTER_DOUBLE; + counters[0].value.vdbl = 0; + + sysprof_capture_writer_define_counters (writer, + SYSPROF_CAPTURE_CURRENT_TIME, + -1, + -1, + counters, + 1); + + for (;;) + { + g_autoptr(DexFuture) read_future = dex_aio_read (NULL, st.stat_fd, buf, sizeof buf-1, 0); + gssize n_read; + + dex_await (dex_future_first (dex_ref (read_future), + dex_ref (record->cancellable), + NULL), + NULL); + + if (dex_future_get_status (read_future) != DEX_FUTURE_STATUS_RESOLVED) + break; + + n_read = dex_await_int64 (dex_ref (read_future), NULL); + if (n_read <= 0) + break; + + if (n_read > 0) + { + buf[n_read] = 0; + + mem_stat_parse (&st, buf); + + sysprof_capture_writer_set_counters (writer, + SYSPROF_CAPTURE_CURRENT_TIME, + -1, + -1, + &counter_id, + &st.sys.used, + 1); + } + + dex_await (dex_future_first (dex_ref (record->cancellable), + dex_timeout_new_usec (G_USEC_PER_SEC / 2), + NULL), + NULL); + + if (dex_future_get_status (record->cancellable) == DEX_FUTURE_STATUS_REJECTED) + break; + } + + if (!mem_stat_close (&st, &error)) + return dex_future_new_for_error (g_steal_pointer (&error)); + + return dex_future_new_for_boolean (TRUE); +} + +static DexFuture * +sysprof_memory_usage_record (SysprofInstrument *instrument, + SysprofRecording *recording, + GCancellable *cancellable) +{ + Record *record; + + g_assert (SYSPROF_IS_MEMORY_USAGE (instrument)); + g_assert (SYSPROF_IS_RECORDING (recording)); + g_assert (G_IS_CANCELLABLE (cancellable)); + + record = g_new0 (Record, 1); + record->recording = g_object_ref (recording); + record->cancellable = dex_cancellable_new_from_cancellable (cancellable); + + return dex_scheduler_spawn (NULL, 0, + sysprof_memory_usage_record_fiber, + record, + record_free); +} + +static void +sysprof_memory_usage_class_init (SysprofMemoryUsageClass *klass) +{ + SysprofInstrumentClass *instrument_class = SYSPROF_INSTRUMENT_CLASS (klass); + + instrument_class->record = sysprof_memory_usage_record; + + keys = g_hash_table_new (g_str_hash, g_str_equal); + +#define ADD_OFFSET(n,o) \ + g_hash_table_insert (keys, (gchar *)n, GUINT_TO_POINTER (o)) + ADD_OFFSET ("MemTotal", G_STRUCT_OFFSET (MemStat, sys.total)); + ADD_OFFSET ("MemFree", G_STRUCT_OFFSET (MemStat, sys.free)); + ADD_OFFSET ("MemAvailable", G_STRUCT_OFFSET (MemStat, sys.avail)); +#undef ADD_OFFSET +} + +static void +sysprof_memory_usage_init (SysprofMemoryUsage *self) +{ +} + +SysprofInstrument * +sysprof_memory_usage_new (void) +{ + return g_object_new (SYSPROF_TYPE_MEMORY_USAGE, NULL); +} diff --git a/src/libsysprof-profile/sysprof-memory-usage.h b/src/libsysprof-profile/sysprof-memory-usage.h new file mode 100644 index 00000000..4b63f868 --- /dev/null +++ b/src/libsysprof-profile/sysprof-memory-usage.h @@ -0,0 +1,42 @@ +/* sysprof-memory-usage.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-instrument.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_MEMORY_USAGE (sysprof_memory_usage_get_type()) +#define SYSPROF_IS_MEMORY_USAGE(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_MEMORY_USAGE) +#define SYSPROF_MEMORY_USAGE(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_MEMORY_USAGE, SysprofMemoryUsage) +#define SYSPROF_MEMORY_USAGE_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_MEMORY_USAGE, SysprofMemoryUsageClass) + +typedef struct _SysprofMemoryUsage SysprofMemoryUsage; +typedef struct _SysprofMemoryUsageClass SysprofMemoryUsageClass; + +SYSPROF_AVAILABLE_IN_ALL +GType sysprof_memory_usage_get_type (void) G_GNUC_CONST; +SYSPROF_AVAILABLE_IN_ALL +SysprofInstrument *sysprof_memory_usage_new (void); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofMemoryUsage, g_object_unref) + +G_END_DECLS diff --git a/src/libsysprof-profile/sysprof-profile.h b/src/libsysprof-profile/sysprof-profile.h index e8aace6d..7a560a98 100644 --- a/src/libsysprof-profile/sysprof-profile.h +++ b/src/libsysprof-profile/sysprof-profile.h @@ -28,6 +28,7 @@ G_BEGIN_DECLS # include "sysprof-cpu-usage.h" # include "sysprof-disk-usage.h" # include "sysprof-instrument.h" +# include "sysprof-memory-usage.h" # include "sysprof-network-usage.h" # include "sysprof-profiler.h" # include "sysprof-recording.h" diff --git a/src/libsysprof-profile/tests/test-profiler.c b/src/libsysprof-profile/tests/test-profiler.c index e451cc38..ba4b1364 100644 --- a/src/libsysprof-profile/tests/test-profiler.c +++ b/src/libsysprof-profile/tests/test-profiler.c @@ -118,6 +118,7 @@ main (int argc, sysprof_profiler_add_instrument (profiler, sysprof_cpu_usage_new ()); sysprof_profiler_add_instrument (profiler, sysprof_disk_usage_new ()); + sysprof_profiler_add_instrument (profiler, sysprof_memory_usage_new ()); sysprof_profiler_add_instrument (profiler, sysprof_network_usage_new ()); for (int i = 1; i < argc; i++) From 25d629beb4ab86c0bc4c29a82bcfd2536e300924 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Sat, 3 Jun 2023 21:28:22 -0700 Subject: [PATCH 0336/1030] libsysprof-profile: start on perf event stream This is a bit different than how we did things previously, but the same mechanics are involved. Instead of multiple CPU registered together, we'll just use one-stream-per-cpu. Partly because I intend to drop support for profiling a single process as that doesn't really get used much nor does it seem to yield very good results from perf. --- src/libsysprof-profile/meson.build | 1 + .../sysprof-perf-event-stream-private.h | 137 ++++++ .../sysprof-perf-event-stream.c | 439 ++++++++++++++++++ 3 files changed, 577 insertions(+) create mode 100644 src/libsysprof-profile/sysprof-perf-event-stream-private.h create mode 100644 src/libsysprof-profile/sysprof-perf-event-stream.c diff --git a/src/libsysprof-profile/meson.build b/src/libsysprof-profile/meson.build index 9969b9ee..ed4deed7 100644 --- a/src/libsysprof-profile/meson.build +++ b/src/libsysprof-profile/meson.build @@ -12,6 +12,7 @@ libsysprof_profile_public_sources = [ libsysprof_profile_private_sources = [ 'mapped-ring-buffer-source.c', 'sysprof-controlfd-instrument.c', + 'sysprof-perf-event-stream.c', 'sysprof-podman.c', 'sysprof-polkit.c', ] diff --git a/src/libsysprof-profile/sysprof-perf-event-stream-private.h b/src/libsysprof-profile/sysprof-perf-event-stream-private.h new file mode 100644 index 00000000..a9bc1987 --- /dev/null +++ b/src/libsysprof-profile/sysprof-perf-event-stream-private.h @@ -0,0 +1,137 @@ +/* sysprof-perf-event-stream-private.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include +#include +#include + +#include + +G_BEGIN_DECLS + +SYSPROF_ALIGNED_BEGIN(1) +typedef struct _SysprofPerfEventFork +{ + struct perf_event_header header; + guint32 pid; + guint32 ppid; + guint32 tid; + guint32 ptid; + guint64 time; +} SysprofPerfEventFork +SYSPROF_ALIGNED_END(1); + +SYSPROF_ALIGNED_BEGIN(1) +typedef struct _SysprofPerfEventComm +{ + struct perf_event_header header; + guint32 pid; + guint32 tid; + gchar comm[0]; +} SysprofPerfEventComm +SYSPROF_ALIGNED_END(1); + +SYSPROF_ALIGNED_BEGIN(1) +typedef struct _SysprofPerfEventExit +{ + struct perf_event_header header; + guint32 pid; + guint32 ppid; + guint32 tid; + guint32 ptid; + guint64 time; +} SysprofPerfEventExit +SYSPROF_ALIGNED_END(1); + +SYSPROF_ALIGNED_BEGIN(1) +typedef struct _SysprofPerfEventMmap +{ + struct perf_event_header header; + guint32 pid; + guint32 tid; + guint64 addr; + guint64 len; + guint64 pgoff; + char filename[0]; +} SysprofPerfEventMmap +SYSPROF_ALIGNED_END(1); + +SYSPROF_ALIGNED_BEGIN(1) +typedef struct _SysprofPerfEventCallchain +{ + struct perf_event_header header; + guint64 identifier; + guint64 ip; + guint32 pid; + guint32 tid; + guint64 time; + guint64 n_ips; + guint64 ips[0]; +} SysprofPerfEventCallchain +SYSPROF_ALIGNED_END(1); + +SYSPROF_ALIGNED_BEGIN(1) +typedef struct _SysprofPerfEventTracepoint +{ + struct perf_event_header header; + guint64 identifier; + guint64 ip; + guint32 pid; + guint32 tid; + guint64 time; + guint32 raw_size; + guchar raw[]; +} SysprofPerfEventTracepoint +SYSPROF_ALIGNED_END(1); + +SYSPROF_ALIGNED_BEGIN(1) +typedef union _SysprofPerfEvent +{ + struct perf_event_header header; + SysprofPerfEventFork fork; + SysprofPerfEventComm comm; + SysprofPerfEventExit exit; + SysprofPerfEventMmap mmap; + SysprofPerfEventCallchain callchain; + SysprofPerfEventTracepoint tracepoint; + guint8 raw[0]; +} SysprofPerfEvent +SYSPROF_ALIGNED_END(1); + +typedef void (*SysprofPerfEventCallback) (const SysprofPerfEvent *event, + guint cpu, + gpointer user_data); + +#define SYSPROF_TYPE_PERF_EVENT_STREAM (sysprof_perf_event_stream_get_type()) + +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, + gulong flags, + SysprofPerfEventCallback callback, + gpointer callback_data, + GDestroyNotify callback_data_destroy); + +G_END_DECLS diff --git a/src/libsysprof-profile/sysprof-perf-event-stream.c b/src/libsysprof-profile/sysprof-perf-event-stream.c new file mode 100644 index 00000000..17a36643 --- /dev/null +++ b/src/libsysprof-profile/sysprof-perf-event-stream.c @@ -0,0 +1,439 @@ +/* sysprof-perf-event-stream.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +/* Sysprof -- Sampling, systemwide CPU profiler + * Copyright 2004, Red Hat, Inc. + * Copyright 2004, 2005, Soeren Sandmann + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include +#include +#include +#include +#ifdef HAVE_STDATOMIC_H +# include +#endif +#include +#include +#include +#include +#include + +#include "sysprof-perf-event-stream-private.h" + +/* + * Number of pages to map for the ring buffer. We map one additional buffer + * at the beginning for header information to communicate with perf. + */ +#define N_PAGES 32 + +struct _SysprofPerfEventStream +{ + GObject parent_instance; + + GDBusConnection *connection; + + GSource *source; + + struct perf_event_attr attr; + + int cpu; + int group_fd; + gulong flags; + + SysprofPerfEventCallback callback; + gpointer callback_data; + GDestroyNotify callback_data_destroy; + + DexPromise *promise; + + int perf_fd; + gpointer perf_fd_tag; + + struct perf_event_mmap_page *map; + guint8 *map_data; + guint64 tail; + + guint active : 1; +}; + +typedef struct _SysprofPerfEventSource +{ + GSource source; + SysprofPerfEventStream *stream; +} SysprofPerfEventSource; + +enum { + PROP_0, + PROP_ACTIVE, + N_PROPS +}; + +G_DEFINE_FINAL_TYPE (SysprofPerfEventStream, sysprof_perf_event_stream, G_TYPE_OBJECT) + +static GParamSpec *properties [N_PROPS]; + +static void +sysprof_perf_event_stream_flush (SysprofPerfEventStream *self) +{ + guint64 n_bytes = N_PAGES * sysprof_getpagesize (); + guint64 mask = n_bytes - 1; + guint64 head; + guint64 tail; + + g_assert (SYSPROF_IS_PERF_EVENT_STREAM (self)); + + tail = self->tail; + head = self->map->data_head; + +#ifdef HAVE_STDATOMIC_H + atomic_thread_fence (memory_order_acquire); +#elif G_GNUC_CHECK_VERSION(3, 0) + __sync_synchronize (); +#endif + + if (head < tail) + tail = head; + + while ((head - tail) >= sizeof (struct perf_event_header)) + { + g_autofree guint8 *free_me = NULL; + struct perf_event_header *header; + guint8 buffer[4096]; + + /* Note that: + * + * - perf events are a multiple of 64 bits + * - the perf event header is 64 bits + * - the data area is a multiple of 64 bits + * + * which means there will always be space for one header, which means we + * can safely dereference the size field. + */ + header = (struct perf_event_header *)(gpointer)(self->map_data + (tail & mask)); + + if (header->size > head - tail) + { + /* The kernel did not generate a complete event. + * I don't think that can happen, but we may as well + * be paranoid. + */ + break; + } + + if (self->map_data + (tail & mask) + header->size > self->map_data + n_bytes) + { + gint n_before; + gint n_after; + guint8 *b; + + if (header->size > sizeof buffer) + free_me = b = g_malloc (header->size); + else + b = buffer; + + n_after = (tail & mask) + header->size - n_bytes; + n_before = header->size - n_after; + + memcpy (b, self->map_data + (tail & mask), n_before); + memcpy (b + n_before, self->map_data, n_after); + + header = (struct perf_event_header *)(gpointer)b; + } + + if (self->callback != NULL) + self->callback ((SysprofPerfEvent *)header, self->cpu, self->callback_data); + + tail += header->size; + } + + self->tail = tail; + +#ifdef HAVE_STDATOMIC_H + atomic_thread_fence (memory_order_seq_cst); +#elif G_GNUC_CHECK_VERSION(3, 0) + __sync_synchronize (); +#endif + + self->map->data_tail = tail; +} + +static gboolean +sysprof_perf_event_source_dispatch (GSource *source, + GSourceFunc callback, + gpointer user_data) +{ + return callback ? callback (user_data) : G_SOURCE_CONTINUE; +} + +static GSourceFuncs source_funcs = { + .dispatch = sysprof_perf_event_source_dispatch, +}; + +static gboolean +sysprof_perf_event_stream_dispatch (gpointer user_data) +{ + SysprofPerfEventStream *self = user_data; + + g_assert (SYSPROF_IS_PERF_EVENT_STREAM (self)); + + sysprof_perf_event_stream_flush (self); + + return G_SOURCE_REMOVE; +} + +static void +sysprof_perf_event_stream_finalize (GObject *object) +{ + SysprofPerfEventStream *self = (SysprofPerfEventStream *)object; + + if (self->callback_data_destroy) + { + self->callback_data_destroy (self->callback_data); + self->callback_data_destroy = NULL; + self->callback_data = NULL; + } + + self->callback = NULL; + + dex_clear (&self->promise); + + g_clear_object (&self->connection); + + if (self->source != NULL) + { + g_source_destroy (self->source); + g_source_unref (self->source); + self->source = NULL; + } + + g_clear_fd (&self->perf_fd, NULL); + + G_OBJECT_CLASS (sysprof_perf_event_stream_parent_class)->finalize (object); +} + +static void +sysprof_perf_event_stream_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofPerfEventStream *self = SYSPROF_PERF_EVENT_STREAM (object); + + switch (prop_id) + { + case PROP_ACTIVE: + g_value_set_boolean (value, self->active); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_perf_event_stream_class_init (SysprofPerfEventStreamClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = sysprof_perf_event_stream_finalize; + object_class->get_property = sysprof_perf_event_stream_get_property; + + properties[PROP_ACTIVE] = + g_param_spec_boolean ("active", NULL, NULL, + FALSE, + (G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); +} + +static void +sysprof_perf_event_stream_init (SysprofPerfEventStream *self) +{ + self->perf_fd = -1; +} + +static void +sysprof_perf_event_stream_new_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + GDBusConnection *connection = (GDBusConnection *)object; + g_autoptr(SysprofPerfEventStream) self = 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 (SYSPROF_IS_PERF_EVENT_STREAM (self)); + + if ((ret = g_dbus_connection_call_with_unix_fd_list_finish (connection, &fd_list, result, &error))) + { + int handle; + int fd; + + g_variant_get (ret, "(h)", &handle); + + if (-1 != (fd = g_unix_fd_list_get (fd_list, handle, &error))) + self->perf_fd = fd; + } + + if (error != NULL) + dex_promise_reject (self->promise, g_steal_pointer (&error)); + else + dex_promise_resolve_object (self->promise, g_object_ref (self)); + + dex_clear (&self->promise); +} + +DexFuture * +sysprof_perf_event_stream_new (GDBusConnection *connection, + struct perf_event_attr *attr, + int cpu, + int group_fd, + gulong flags, + SysprofPerfEventCallback callback, + gpointer callback_data, + GDestroyNotify callback_data_destroy) +{ + g_autoptr(SysprofPerfEventStream) self = NULL; + g_autoptr(GUnixFDList) fd_list = NULL; + g_autoptr(DexPromise) promise = NULL; + GVariantDict options = G_VARIANT_DICT_INIT (NULL); + int group_fd_handle = -1; + + g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL); + g_return_val_if_fail (attr != NULL, NULL); + g_return_val_if_fail (cpu > -1, NULL); + g_return_val_if_fail (group_fd >= -1, NULL); + + promise = dex_promise_new (); + + self = g_object_new (SYSPROF_TYPE_PERF_EVENT_STREAM, NULL); + self->connection = g_object_ref (connection); + self->attr = *attr; + self->cpu = cpu; + self->group_fd = group_fd; + self->flags = flags; + self->callback = callback; + self->callback_data = callback_data; + self->callback_data_destroy = callback_data_destroy; + self->promise = dex_ref (promise); + self->source = g_source_new (&source_funcs, sizeof (SysprofPerfEventSource)); + + g_source_set_callback (self->source, sysprof_perf_event_stream_dispatch, self, NULL); + g_source_set_name (self->source, "[perf]"); + g_source_attach (self->source, NULL); + + fd_list = g_unix_fd_list_new (); + + if (group_fd > -1) + group_fd_handle = g_unix_fd_list_append (fd_list, group_fd, NULL); + + g_dbus_connection_call_with_unix_fd_list (connection, + "org.gnome.Sysprof3", + "/org/gnome/Sysprof3/Service", + "org.gnome.Sysprof3.Service", + "PerfEventOpen", + g_variant_new ("(@a{sv}ii@ht)", + g_variant_dict_end (&options), + -1, + cpu, + group_fd_handle, + flags), + G_VARIANT_TYPE ("(h)"), + G_DBUS_CALL_FLAGS_NONE, + G_MAXUINT, + fd_list, + dex_promise_get_cancellable (promise), + sysprof_perf_event_stream_new_cb, + g_object_ref (self)); + + return DEX_FUTURE (g_steal_pointer (&promise)); +} + +gboolean +sysprof_perf_event_stream_enable (SysprofPerfEventStream *self, + GError **error) +{ + g_return_val_if_fail (SYSPROF_IS_PERF_EVENT_STREAM (self), FALSE); + + if (self->active) + return TRUE; + + if (0 != ioctl (self->perf_fd, PERF_EVENT_IOC_ENABLE)) + { + int errsv = errno; + g_set_error_literal (error, + G_IO_ERROR, + g_io_error_from_errno (errsv), + g_strerror (errsv)); + return FALSE; + } + + self->active = TRUE; + + g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ACTIVE]); + + return TRUE; +} + +gboolean +sysprof_perf_event_stream_disable (SysprofPerfEventStream *self, + GError **error) +{ + g_return_val_if_fail (SYSPROF_IS_PERF_EVENT_STREAM (self), FALSE); + + if (!self->active) + return TRUE; + + if (0 != ioctl (self->perf_fd, PERF_EVENT_IOC_DISABLE)) + { + int errsv = errno; + g_set_error_literal (error, + G_IO_ERROR, + g_io_error_from_errno (errsv), + g_strerror (errsv)); + return FALSE; + } + + self->active = FALSE; + + g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ACTIVE]); + + return TRUE; +} From d1db76a08df1c53d883055742603203da444a9dd Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Sat, 3 Jun 2023 23:12:54 -0700 Subject: [PATCH 0337/1030] libsysprof-profile: make enable/disable accessible --- .../sysprof-perf-event-stream-private.h | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/libsysprof-profile/sysprof-perf-event-stream-private.h b/src/libsysprof-profile/sysprof-perf-event-stream-private.h index a9bc1987..aab37632 100644 --- a/src/libsysprof-profile/sysprof-perf-event-stream-private.h +++ b/src/libsysprof-profile/sysprof-perf-event-stream-private.h @@ -125,13 +125,17 @@ 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, - gulong flags, - SysprofPerfEventCallback callback, - gpointer callback_data, - GDestroyNotify callback_data_destroy); +DexFuture *sysprof_perf_event_stream_new (GDBusConnection *connection, + struct perf_event_attr *attr, + int cpu, + int group_fd, + gulong 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); G_END_DECLS From ece30b52b13537fa72fdfe9013dd4278cbb8883f Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Sat, 3 Jun 2023 23:13:12 -0700 Subject: [PATCH 0338/1030] libsysprof-profile: fix GDBusConnection usage --- src/libsysprof-profile/sysprof-perf-event-stream.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libsysprof-profile/sysprof-perf-event-stream.c b/src/libsysprof-profile/sysprof-perf-event-stream.c index 17a36643..dc9bf0c0 100644 --- a/src/libsysprof-profile/sysprof-perf-event-stream.c +++ b/src/libsysprof-profile/sysprof-perf-event-stream.c @@ -366,15 +366,15 @@ sysprof_perf_event_stream_new (GDBusConnection *connection, g_dbus_connection_call_with_unix_fd_list (connection, "org.gnome.Sysprof3", - "/org/gnome/Sysprof3/Service", + "/org/gnome/Sysprof3", "org.gnome.Sysprof3.Service", "PerfEventOpen", - g_variant_new ("(@a{sv}ii@ht)", + g_variant_new ("(@a{sv}iiht)", g_variant_dict_end (&options), -1, cpu, group_fd_handle, - flags), + (guint64)flags), G_VARIANT_TYPE ("(h)"), G_DBUS_CALL_FLAGS_NONE, G_MAXUINT, From fb81867bf5148d2576e503af66623ba368dc7280 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Sat, 3 Jun 2023 23:14:14 -0700 Subject: [PATCH 0339/1030] libsysprof-profiler: start on perf instrument This starts porting the old perf code from libsysprof into libsysprof-profile. It does not bring over tracepoints yet, because we need to think about how we want to support that. I may opt to do that as a separate instrument even if that takes an additional perf fd. --- src/libsysprof-profile/meson.build | 2 + src/libsysprof-profile/sysprof-profile.h | 1 + src/libsysprof-profile/sysprof-sampler.c | 408 +++++++++++++++++++ src/libsysprof-profile/sysprof-sampler.h | 42 ++ src/libsysprof-profile/tests/test-profiler.c | 1 + 5 files changed, 454 insertions(+) create mode 100644 src/libsysprof-profile/sysprof-sampler.c create mode 100644 src/libsysprof-profile/sysprof-sampler.h diff --git a/src/libsysprof-profile/meson.build b/src/libsysprof-profile/meson.build index ed4deed7..340cefd5 100644 --- a/src/libsysprof-profile/meson.build +++ b/src/libsysprof-profile/meson.build @@ -6,6 +6,7 @@ libsysprof_profile_public_sources = [ 'sysprof-network-usage.c', 'sysprof-profiler.c', 'sysprof-recording.c', + 'sysprof-sampler.c', 'sysprof-spawnable.c', ] @@ -27,6 +28,7 @@ libsysprof_profile_public_headers = [ 'sysprof-network-usage.h', 'sysprof-profiler.h', 'sysprof-recording.h', + 'sysprof-sampler.h', 'sysprof-spawnable.h', ] diff --git a/src/libsysprof-profile/sysprof-profile.h b/src/libsysprof-profile/sysprof-profile.h index 7a560a98..8ad775e2 100644 --- a/src/libsysprof-profile/sysprof-profile.h +++ b/src/libsysprof-profile/sysprof-profile.h @@ -32,6 +32,7 @@ G_BEGIN_DECLS # include "sysprof-network-usage.h" # include "sysprof-profiler.h" # include "sysprof-recording.h" +# include "sysprof-sampler.h" # include "sysprof-spawnable.h" #undef SYSPROF_PROFILE_INSIDE diff --git a/src/libsysprof-profile/sysprof-sampler.c b/src/libsysprof-profile/sysprof-sampler.c new file mode 100644 index 00000000..530ecd80 --- /dev/null +++ b/src/libsysprof-profile/sysprof-sampler.c @@ -0,0 +1,408 @@ +/* sysprof-sampler.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-instrument-private.h" +#include "sysprof-perf-event-stream-private.h" +#include "sysprof-recording-private.h" +#include "sysprof-sampler.h" + +#define N_WAKEUP_EVENTS 149 + +struct _SysprofSampler +{ + SysprofInstrument parent_instance; + GPtrArray *perf_event_streams; +}; + +struct _SysprofSamplerClass +{ + SysprofInstrumentClass parent_class; +}; + +G_DEFINE_FINAL_TYPE (SysprofSampler, sysprof_sampler, SYSPROF_TYPE_INSTRUMENT) + +static char ** +sysprof_sampler_list_required_policy (SysprofInstrument *instrument) +{ + static const char *policy[] = {"org.gnome.sysprof3.profile", NULL}; + + return g_strdupv ((char **)policy); +} + +static inline void +realign (gsize *pos, + gsize align) +{ + *pos = (*pos + align - 1) & ~(align - 1); +} + +static void +sysprof_sampler_add_callback (SysprofCaptureWriter *writer, + int cpu, + const SysprofPerfEventCallchain *sample) +{ + const guint64 *ips; + guint64 trace[3]; + int n_ips; + + g_assert (writer != NULL); + g_assert (sample != NULL); + + ips = sample->ips; + n_ips = sample->n_ips; + + if (n_ips == 0) + { + if (sample->header.misc & PERF_RECORD_MISC_KERNEL) + { + trace[0] = PERF_CONTEXT_KERNEL; + trace[1] = sample->ip; + trace[2] = PERF_CONTEXT_USER; + + ips = trace; + n_ips = 3; + } + else + { + trace[0] = PERF_CONTEXT_USER; + trace[1] = sample->ip; + + ips = trace; + n_ips = 2; + } + } + + sysprof_capture_writer_add_sample (writer, + sample->time, + cpu, + sample->pid, + sample->tid, + ips, + n_ips); +} + +static void +sysprof_sampler_perf_event_stream_cb (const SysprofPerfEvent *event, + guint cpu, + gpointer user_data) +{ + SysprofCaptureWriter *writer = user_data; + gsize offset; + gint64 time; + + g_assert (writer != NULL); + g_assert (event != NULL); + + switch (event->header.type) + { + case PERF_RECORD_COMM: + offset = strlen (event->comm.comm) + 1; + realign (&offset, sizeof (guint64)); + offset += sizeof (GPid) + sizeof (GPid); + memcpy (&time, event->comm.comm + offset, sizeof time); + + if (event->comm.pid == event->comm.tid) + sysprof_capture_writer_add_process (writer, + time, + cpu, + event->comm.pid, + event->comm.comm); + + break; + + case PERF_RECORD_EXIT: + /* Ignore fork exits for now */ + if (event->exit.tid != event->exit.pid) + break; + + sysprof_capture_writer_add_exit (writer, + event->exit.time, + cpu, + event->exit.pid); + + break; + + case PERF_RECORD_FORK: + sysprof_capture_writer_add_fork (writer, + event->fork.time, + cpu, + event->fork.ptid, + event->fork.tid); + + /* + * TODO: We should add support for "follow fork" of the GPid if we are + * targetting it. + */ + + break; + + case PERF_RECORD_LOST: + break; + + case PERF_RECORD_MMAP: + offset = strlen (event->mmap.filename) + 1; + realign (&offset, sizeof (guint64)); + offset += sizeof (GPid) + sizeof (GPid); + memcpy (&time, event->mmap.filename + offset, sizeof time); + + sysprof_capture_writer_add_map (writer, + time, + cpu, + event->mmap.pid, + event->mmap.addr, + event->mmap.addr + event->mmap.len, + event->mmap.pgoff, + 0, + event->mmap.filename); + + break; + + case PERF_RECORD_READ: + break; + + case PERF_RECORD_SAMPLE: + sysprof_sampler_add_callback (writer, cpu, &event->callchain); + break; + + case PERF_RECORD_THROTTLE: + case PERF_RECORD_UNTHROTTLE: + default: + break; + } +} + +typedef struct _Prepare +{ + SysprofRecording *recording; + SysprofSampler *sampler; +} Prepare; + +static void +prepare_free (Prepare *prepare) +{ + g_clear_object (&prepare->recording); + g_clear_object (&prepare->sampler); + g_free (prepare); +} + +static DexFuture * +sysprof_sampler_prepare_fiber (gpointer user_data) +{ + Prepare *prepare = user_data; + SysprofCaptureWriter *writer; + g_autoptr(GDBusConnection) connection = NULL; + g_autoptr(GPtrArray) futures = NULL; + g_autoptr(GError) error = NULL; + struct perf_event_attr attr = {0}; + guint n_cpu; + + g_assert (prepare != NULL); + g_assert (SYSPROF_IS_RECORDING (prepare->recording)); + g_assert (SYSPROF_IS_SAMPLER (prepare->sampler)); + + n_cpu = g_get_num_processors (); + futures = g_ptr_array_new_with_free_func (dex_unref); + writer = _sysprof_recording_writer (prepare->recording); + + attr.sample_type = PERF_SAMPLE_IP + | PERF_SAMPLE_TID + | PERF_SAMPLE_IDENTIFIER + | PERF_SAMPLE_CALLCHAIN + | PERF_SAMPLE_TIME; + attr.wakeup_events = N_WAKEUP_EVENTS; + attr.disabled = TRUE; + attr.mmap = 1; + 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.size = sizeof attr; + + attr.type = PERF_TYPE_HARDWARE; + attr.config = PERF_COUNT_HW_CPU_CYCLES; + attr.sample_period = 1200000; + + if (!(connection = dex_await_object (dex_bus_get (G_BUS_TYPE_SYSTEM), &error))) + return dex_future_new_for_error (g_steal_pointer (&error)); + + for (guint i = 0; i < n_cpu; i++) + g_ptr_array_add (futures, + sysprof_perf_event_stream_new (connection, + &attr, + i, + -1, + 0, + sysprof_sampler_perf_event_stream_cb, + sysprof_capture_writer_ref (writer), + (GDestroyNotify)sysprof_capture_writer_unref)); + + if (!dex_await (dex_future_allv ((DexFuture **)futures->pdata, futures->len), &error)) + return dex_future_new_for_error (g_steal_pointer (&error)); + + for (guint i = 0; i < futures->len; i++) + { + DexFuture *future = g_ptr_array_index (futures, i); + g_autoptr(SysprofPerfEventStream) stream = NULL; + g_autoptr(GError) stream_error = NULL; + + if ((stream = dex_await_object (dex_ref (future), &stream_error))) + g_ptr_array_add (prepare->sampler->perf_event_streams, g_steal_pointer (&stream)); + } + + return dex_future_new_for_boolean (TRUE); +} + +static DexFuture * +sysprof_sampler_prepare (SysprofInstrument *instrument, + SysprofRecording *recording) +{ + SysprofSampler *self = (SysprofSampler *)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); + + return dex_scheduler_spawn (NULL, 0, + sysprof_sampler_prepare_fiber, + prepare, + (GDestroyNotify)prepare_free); +} + +typedef struct _Record +{ + SysprofRecording *recording; + SysprofSampler *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_sampler_record_fiber (gpointer user_data) +{ + Record *record = user_data; + + g_assert (record != NULL); + g_assert (SYSPROF_IS_SAMPLER (record->sampler)); + g_assert (SYSPROF_IS_RECORDING (record->recording)); + g_assert (DEX_IS_FUTURE (record->cancellable)); + + for (guint i = 0; i < record->sampler->perf_event_streams->len; i++) + { + SysprofPerfEventStream *stream = g_ptr_array_index (record->sampler->perf_event_streams, i); + g_autoptr(GError) error = NULL; + + if (!sysprof_perf_event_stream_enable (stream, &error)) + g_debug ("%s", error->message); + g_debug ("Sampler %d enabled", i); + } + + dex_await (dex_ref (record->cancellable), NULL); + + for (guint i = 0; i < record->sampler->perf_event_streams->len; i++) + { + SysprofPerfEventStream *stream = g_ptr_array_index (record->sampler->perf_event_streams, i); + g_autoptr(GError) error = NULL; + + if (!sysprof_perf_event_stream_disable (stream, &error)) + g_debug ("%s", error->message); + else + g_debug ("Sampler %d disabled", i); + } + + return dex_future_new_for_boolean (TRUE); +} + +static DexFuture * +sysprof_sampler_record (SysprofInstrument *instrument, + SysprofRecording *recording, + GCancellable *cancellable) +{ + SysprofSampler *self = (SysprofSampler *)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_sampler_record_fiber, + record, + (GDestroyNotify)record_free); +} + +static void +sysprof_sampler_finalize (GObject *object) +{ + SysprofSampler *self = (SysprofSampler *)object; + + g_clear_pointer (&self->perf_event_streams, g_ptr_array_unref); + + G_OBJECT_CLASS (sysprof_sampler_parent_class)->finalize (object); +} + +static void +sysprof_sampler_class_init (SysprofSamplerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + SysprofInstrumentClass *instrument_class = SYSPROF_INSTRUMENT_CLASS (klass); + + object_class->finalize = sysprof_sampler_finalize; + + instrument_class->list_required_policy = sysprof_sampler_list_required_policy; + instrument_class->prepare = sysprof_sampler_prepare; + instrument_class->record = sysprof_sampler_record; +} + +static void +sysprof_sampler_init (SysprofSampler *self) +{ + self->perf_event_streams = g_ptr_array_new_with_free_func (g_object_unref); +} + +SysprofInstrument * +sysprof_sampler_new (void) +{ + return g_object_new (SYSPROF_TYPE_SAMPLER, NULL); +} diff --git a/src/libsysprof-profile/sysprof-sampler.h b/src/libsysprof-profile/sysprof-sampler.h new file mode 100644 index 00000000..4a7309b7 --- /dev/null +++ b/src/libsysprof-profile/sysprof-sampler.h @@ -0,0 +1,42 @@ +/* sysprof-sampler.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-instrument.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_SAMPLER (sysprof_sampler_get_type()) +#define SYSPROF_IS_SAMPLER(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_SAMPLER) +#define SYSPROF_SAMPLER(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_SAMPLER, SysprofSampler) +#define SYSPROF_SAMPLER_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_SAMPLER, SysprofSamplerClass) + +typedef struct _SysprofSampler SysprofSampler; +typedef struct _SysprofSamplerClass SysprofSamplerClass; + +SYSPROF_AVAILABLE_IN_ALL +GType sysprof_sampler_get_type (void) G_GNUC_CONST; +SYSPROF_AVAILABLE_IN_ALL +SysprofInstrument *sysprof_sampler_new (void); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofSampler, g_object_unref) + +G_END_DECLS diff --git a/src/libsysprof-profile/tests/test-profiler.c b/src/libsysprof-profile/tests/test-profiler.c index ba4b1364..a57adbee 100644 --- a/src/libsysprof-profile/tests/test-profiler.c +++ b/src/libsysprof-profile/tests/test-profiler.c @@ -120,6 +120,7 @@ main (int argc, sysprof_profiler_add_instrument (profiler, sysprof_disk_usage_new ()); sysprof_profiler_add_instrument (profiler, sysprof_memory_usage_new ()); sysprof_profiler_add_instrument (profiler, sysprof_network_usage_new ()); + sysprof_profiler_add_instrument (profiler, sysprof_sampler_new ()); for (int i = 1; i < argc; i++) { From 5b5916bdcdac84ff36b0ab27eef9ec6915faffe8 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Sat, 3 Jun 2023 23:53:38 -0700 Subject: [PATCH 0340/1030] libsysprof-profiler: convert perf event attr to variant --- .../sysprof-perf-event-stream.c | 44 ++++++++++++++++++- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/src/libsysprof-profile/sysprof-perf-event-stream.c b/src/libsysprof-profile/sysprof-perf-event-stream.c index dc9bf0c0..4c5ee0b8 100644 --- a/src/libsysprof-profile/sysprof-perf-event-stream.c +++ b/src/libsysprof-profile/sysprof-perf-event-stream.c @@ -106,6 +106,44 @@ 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) +{ + return g_variant_take_ref ( + g_variant_new_parsed ("[" + "{'comm', <%b>}," +#ifdef HAVE_PERF_CLOCKID + "{'clockid', <%i>}," + "{'use_clockid', <%b>}," +#endif + "{'config', <%t>}," + "{'disabled', <%b>}," + "{'exclude_idle', <%b>}," + "{'mmap', <%b>}," + "{'wakeup_events', <%u>}," + "{'sample_id_all', <%b>}," + "{'sample_period', <%t>}," + "{'sample_type', <%t>}," + "{'task', <%b>}," + "{'type', <%u>}" + "]", + (gboolean)!!attr->comm, +#ifdef HAVE_PERF_CLOCKID + (gint32)attr->clockid, + (gboolean)!!attr->use_clockid, +#endif + (guint64)attr->config, + (gboolean)!!attr->disabled, + (gboolean)!!attr->exclude_idle, + (gboolean)!!attr->mmap, + (guint32)attr->wakeup_events, + (gboolean)!!attr->sample_id_all, + (guint64)attr->sample_period, + (guint64)attr->sample_type, + (gboolean)!!attr->task, + (guint32)attr->type)); +} + static void sysprof_perf_event_stream_flush (SysprofPerfEventStream *self) { @@ -333,7 +371,7 @@ sysprof_perf_event_stream_new (GDBusConnection *connection, g_autoptr(SysprofPerfEventStream) self = NULL; g_autoptr(GUnixFDList) fd_list = NULL; g_autoptr(DexPromise) promise = NULL; - GVariantDict options = G_VARIANT_DICT_INIT (NULL); + g_autoptr(GVariant) options = NULL; int group_fd_handle = -1; g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL); @@ -364,13 +402,15 @@ sysprof_perf_event_stream_new (GDBusConnection *connection, if (group_fd > -1) group_fd_handle = g_unix_fd_list_append (fd_list, group_fd, NULL); + options = build_options_dict (attr); + 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)", - g_variant_dict_end (&options), + options, -1, cpu, group_fd_handle, From a6ca0eb2d74c9a070c5894e95f203e894400020a Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Sun, 4 Jun 2023 00:02:58 -0700 Subject: [PATCH 0341/1030] libsysprof-profile: setup mmap for perf mapping --- .../sysprof-perf-event-stream.c | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/src/libsysprof-profile/sysprof-perf-event-stream.c b/src/libsysprof-profile/sysprof-perf-event-stream.c index 4c5ee0b8..26025959 100644 --- a/src/libsysprof-profile/sysprof-perf-event-stream.c +++ b/src/libsysprof-profile/sysprof-perf-event-stream.c @@ -347,7 +347,31 @@ sysprof_perf_event_stream_new_cb (GObject *object, g_variant_get (ret, "(h)", &handle); if (-1 != (fd = g_unix_fd_list_get (fd_list, handle, &error))) - self->perf_fd = fd; + { + gsize map_size; + guint8 *map; + + self->perf_fd = fd; + self->perf_fd_tag = g_source_add_unix_fd (self->source, fd, G_IO_ERR); + + map_size = N_PAGES * sysprof_getpagesize () + sysprof_getpagesize (); + map = mmap (NULL, map_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + + if ((gpointer)map == MAP_FAILED) + { + int errsv = errno; + g_set_error_literal (&error, + G_IO_ERROR, + g_io_error_from_errno (errsv), + g_strerror (errsv)); + } + else + { + self->map = (gpointer)map; + self->map_data = map + sysprof_getpagesize (); + self->tail = 0; + } + } } if (error != NULL) @@ -447,6 +471,8 @@ sysprof_perf_event_stream_enable (SysprofPerfEventStream *self, self->active = TRUE; + g_source_modify_unix_fd (self->source, self->perf_fd_tag, G_IO_IN); + g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ACTIVE]); return TRUE; @@ -473,6 +499,8 @@ sysprof_perf_event_stream_disable (SysprofPerfEventStream *self, self->active = FALSE; + g_source_modify_unix_fd (self->source, self->perf_fd_tag, G_IO_ERR); + g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ACTIVE]); return TRUE; From 8138ab49c3779d9069378316f1f24ddb920673fa Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Sun, 4 Jun 2023 00:04:52 -0700 Subject: [PATCH 0342/1030] libsysprof-profile: always continue the source --- src/libsysprof-profile/sysprof-perf-event-stream.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libsysprof-profile/sysprof-perf-event-stream.c b/src/libsysprof-profile/sysprof-perf-event-stream.c index 26025959..68e60a64 100644 --- a/src/libsysprof-profile/sysprof-perf-event-stream.c +++ b/src/libsysprof-profile/sysprof-perf-event-stream.c @@ -250,7 +250,7 @@ sysprof_perf_event_stream_dispatch (gpointer user_data) sysprof_perf_event_stream_flush (self); - return G_SOURCE_REMOVE; + return G_SOURCE_CONTINUE; } static void From a3c235a31608a39d86d7c1546ff40d6a92bd69b2 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Sun, 4 Jun 2023 10:20:34 -0700 Subject: [PATCH 0343/1030] libsysprof-profile: don't use FD to drive perf source It is not enough to get writability, as that wont trigger on the perf source. Instead, we need to check the mmap'd header and drive things off of that. It might be nice to eventually determine how many of the samples are from our own process and back-off our timeout based on that. --- .../sysprof-perf-event-stream.c | 37 ++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/src/libsysprof-profile/sysprof-perf-event-stream.c b/src/libsysprof-profile/sysprof-perf-event-stream.c index 68e60a64..854793d7 100644 --- a/src/libsysprof-profile/sysprof-perf-event-stream.c +++ b/src/libsysprof-profile/sysprof-perf-event-stream.c @@ -229,6 +229,34 @@ sysprof_perf_event_stream_flush (SysprofPerfEventStream *self) self->map->data_tail = tail; } +static gboolean +sysprof_perf_event_source_prepare (GSource *gsource, + int *timeout) +{ + SysprofPerfEventSource *source = (SysprofPerfEventSource *)gsource; + SysprofPerfEventStream *self = source->stream; + + if (timeout != NULL) + *timeout = 5; + + return self != NULL && + self->active && + self->map != NULL && + self->tail != self->map->data_head; +} + +static gboolean +sysprof_perf_event_source_check (GSource *gsource) +{ + SysprofPerfEventSource *source = (SysprofPerfEventSource *)gsource; + SysprofPerfEventStream *self = source->stream; + + return self != NULL && + self->active && + self->map != NULL && + self->tail != self->map->data_head; +} + static gboolean sysprof_perf_event_source_dispatch (GSource *source, GSourceFunc callback, @@ -238,6 +266,8 @@ sysprof_perf_event_source_dispatch (GSource *source, } static GSourceFuncs source_funcs = { + .prepare = sysprof_perf_event_source_prepare, + .check = sysprof_perf_event_source_check, .dispatch = sysprof_perf_event_source_dispatch, }; @@ -392,6 +422,7 @@ sysprof_perf_event_stream_new (GDBusConnection *connection, gpointer callback_data, GDestroyNotify callback_data_destroy) { + SysprofPerfEventSource *source; g_autoptr(SysprofPerfEventStream) self = NULL; g_autoptr(GUnixFDList) fd_list = NULL; g_autoptr(DexPromise) promise = NULL; @@ -415,7 +446,11 @@ sysprof_perf_event_stream_new (GDBusConnection *connection, self->callback_data = callback_data; self->callback_data_destroy = callback_data_destroy; self->promise = dex_ref (promise); - self->source = g_source_new (&source_funcs, sizeof (SysprofPerfEventSource)); + + source = (SysprofPerfEventSource *) + g_source_new (&source_funcs, sizeof (SysprofPerfEventSource)); + source->stream = self; + self->source = (GSource *)source; g_source_set_callback (self->source, sysprof_perf_event_stream_dispatch, self, NULL); g_source_set_name (self->source, "[perf]"); From 13b76f7de6426359a2c62e6bdd41d282f2e915bf Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Sun, 4 Jun 2023 12:35:04 -0700 Subject: [PATCH 0344/1030] libsysprof-profile: make timeout msec tweakable We may want to tweak this as we discover we can either backoff or increase our timing intervals. --- src/libsysprof-profile/sysprof-perf-event-stream.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libsysprof-profile/sysprof-perf-event-stream.c b/src/libsysprof-profile/sysprof-perf-event-stream.c index 854793d7..abded56c 100644 --- a/src/libsysprof-profile/sysprof-perf-event-stream.c +++ b/src/libsysprof-profile/sysprof-perf-event-stream.c @@ -94,6 +94,7 @@ typedef struct _SysprofPerfEventSource { GSource source; SysprofPerfEventStream *stream; + int timeout_msec; } SysprofPerfEventSource; enum { @@ -237,7 +238,7 @@ sysprof_perf_event_source_prepare (GSource *gsource, SysprofPerfEventStream *self = source->stream; if (timeout != NULL) - *timeout = 5; + *timeout = source->timeout_msec; return self != NULL && self->active && @@ -450,6 +451,7 @@ sysprof_perf_event_stream_new (GDBusConnection *connection, source = (SysprofPerfEventSource *) g_source_new (&source_funcs, sizeof (SysprofPerfEventSource)); source->stream = self; + source->timeout_msec = 5; self->source = (GSource *)source; g_source_set_callback (self->source, sysprof_perf_event_stream_dispatch, self, NULL); From 4f4f3145c1ba1a3caeb895f0e21f9ba38e24ae47 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Sun, 4 Jun 2023 13:28:35 -0700 Subject: [PATCH 0345/1030] libsysprof-profile: backoff perf GSource when possible We have a fairly large buffer for perf events, so we should be able to process these much less frequently to help reduce the process performing the profile from showing up in the profiling results. --- .../sysprof-perf-event-stream.c | 138 +++++++++++------- 1 file changed, 84 insertions(+), 54 deletions(-) diff --git a/src/libsysprof-profile/sysprof-perf-event-stream.c b/src/libsysprof-profile/sysprof-perf-event-stream.c index abded56c..91e4b45b 100644 --- a/src/libsysprof-profile/sysprof-perf-event-stream.c +++ b/src/libsysprof-profile/sysprof-perf-event-stream.c @@ -70,6 +70,7 @@ struct _SysprofPerfEventStream struct perf_event_attr attr; + int self_pid; int cpu; int group_fd; gulong flags; @@ -81,7 +82,6 @@ struct _SysprofPerfEventStream DexPromise *promise; int perf_fd; - gpointer perf_fd_tag; struct perf_event_mmap_page *map; guint8 *map_data; @@ -94,6 +94,7 @@ typedef struct _SysprofPerfEventSource { GSource source; SysprofPerfEventStream *stream; + gint64 next_ready_time; int timeout_msec; } SysprofPerfEventSource; @@ -148,10 +149,14 @@ build_options_dict (const struct perf_event_attr *attr) static void sysprof_perf_event_stream_flush (SysprofPerfEventStream *self) { + SysprofPerfEventSource *source = (SysprofPerfEventSource *)self->source; guint64 n_bytes = N_PAGES * sysprof_getpagesize (); guint64 mask = n_bytes - 1; guint64 head; guint64 tail; + gboolean lost_records = FALSE; + guint us = 0; + guint them = 0; g_assert (SYSPROF_IS_PERF_EVENT_STREAM (self)); @@ -170,8 +175,10 @@ sysprof_perf_event_stream_flush (SysprofPerfEventStream *self) while ((head - tail) >= sizeof (struct perf_event_header)) { g_autofree guint8 *free_me = NULL; + const SysprofPerfEvent *event; struct perf_event_header *header; guint8 buffer[4096]; + gboolean is_self = FALSE; /* Note that: * @@ -213,9 +220,38 @@ sysprof_perf_event_stream_flush (SysprofPerfEventStream *self) header = (struct perf_event_header *)(gpointer)b; } - if (self->callback != NULL) - self->callback ((SysprofPerfEvent *)header, self->cpu, self->callback_data); + event = (SysprofPerfEvent *)header; + switch (event->header.type) + { + default: + case PERF_RECORD_COMM: + case PERF_RECORD_EXIT: + case PERF_RECORD_FORK: + break; + + case PERF_RECORD_SAMPLE: + is_self = event->callchain.pid == self->self_pid; + break; + + case PERF_RECORD_READ: + case PERF_RECORD_THROTTLE: + case PERF_RECORD_UNTHROTTLE: + goto skip_callback; + + case PERF_RECORD_LOST: + lost_records = TRUE; + g_debug ("Lost records from perf"); + break; + } + + if (self->callback != NULL) + self->callback (event, self->cpu, self->callback_data); + + us += is_self; + them += !is_self; + + skip_callback: tail += header->size; } @@ -228,62 +264,56 @@ sysprof_perf_event_stream_flush (SysprofPerfEventStream *self) #endif self->map->data_tail = tail; + + /* If we lost records them we took too long to process events and + * need to speed up how often we process incoming records. However, + * if we are the cause of that (due to running to frequently), then + * we need to back-off. + */ + if (lost_records && us < them/3) + { + if (source->timeout_msec > 1) + source->timeout_msec -= 5; + } + else + { + if (source->timeout_msec < 500) + { + if (us < 5 && them < 5) + source->timeout_msec += 100; + else + source->timeout_msec += 10; + } + } } static gboolean -sysprof_perf_event_source_prepare (GSource *gsource, - int *timeout) -{ - SysprofPerfEventSource *source = (SysprofPerfEventSource *)gsource; - SysprofPerfEventStream *self = source->stream; - - if (timeout != NULL) - *timeout = source->timeout_msec; - - return self != NULL && - self->active && - self->map != NULL && - self->tail != self->map->data_head; -} - -static gboolean -sysprof_perf_event_source_check (GSource *gsource) -{ - SysprofPerfEventSource *source = (SysprofPerfEventSource *)gsource; - SysprofPerfEventStream *self = source->stream; - - return self != NULL && - self->active && - self->map != NULL && - self->tail != self->map->data_head; -} - -static gboolean -sysprof_perf_event_source_dispatch (GSource *source, +sysprof_perf_event_source_dispatch (GSource *gsource, GSourceFunc callback, gpointer user_data) { - return callback ? callback (user_data) : G_SOURCE_CONTINUE; -} + SysprofPerfEventSource *source = (SysprofPerfEventSource *)gsource; + SysprofPerfEventStream *self = source->stream; -static GSourceFuncs source_funcs = { - .prepare = sysprof_perf_event_source_prepare, - .check = sysprof_perf_event_source_check, - .dispatch = sysprof_perf_event_source_dispatch, -}; + if (source->next_ready_time <= g_source_get_time (gsource) && + self != NULL && + self->active && + self->map != NULL && + self->tail != self->map->data_head) + sysprof_perf_event_stream_flush (self); + else + source->timeout_msec = MIN (500, source->timeout_msec + 50); -static gboolean -sysprof_perf_event_stream_dispatch (gpointer user_data) -{ - SysprofPerfEventStream *self = user_data; - - g_assert (SYSPROF_IS_PERF_EVENT_STREAM (self)); - - sysprof_perf_event_stream_flush (self); + source->next_ready_time = g_get_monotonic_time () + (source->timeout_msec * 1000); + g_source_set_ready_time (self->source, source->next_ready_time); return G_SOURCE_CONTINUE; } +static GSourceFuncs source_funcs = { + .dispatch = sysprof_perf_event_source_dispatch, +}; + static void sysprof_perf_event_stream_finalize (GObject *object) { @@ -353,6 +383,7 @@ static void sysprof_perf_event_stream_init (SysprofPerfEventStream *self) { self->perf_fd = -1; + self->self_pid = getpid (); } static void @@ -383,7 +414,6 @@ sysprof_perf_event_stream_new_cb (GObject *object, guint8 *map; self->perf_fd = fd; - self->perf_fd_tag = g_source_add_unix_fd (self->source, fd, G_IO_ERR); map_size = N_PAGES * sysprof_getpagesize () + sysprof_getpagesize (); map = mmap (NULL, map_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); @@ -428,6 +458,7 @@ sysprof_perf_event_stream_new (GDBusConnection *connection, g_autoptr(GUnixFDList) fd_list = NULL; g_autoptr(DexPromise) promise = NULL; g_autoptr(GVariant) options = NULL; + g_autofree char *name = NULL; int group_fd_handle = -1; g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL); @@ -452,10 +483,13 @@ sysprof_perf_event_stream_new (GDBusConnection *connection, g_source_new (&source_funcs, sizeof (SysprofPerfEventSource)); source->stream = self; source->timeout_msec = 5; + source->next_ready_time = g_get_monotonic_time () + (source->timeout_msec * 1000); self->source = (GSource *)source; - g_source_set_callback (self->source, sysprof_perf_event_stream_dispatch, self, NULL); - g_source_set_name (self->source, "[perf]"); + name = g_strdup_printf ("[perf cpu%d]", cpu); + + g_source_set_ready_time (self->source, source->next_ready_time); + g_source_set_name (self->source, name); g_source_attach (self->source, NULL); fd_list = g_unix_fd_list_new (); @@ -508,8 +542,6 @@ sysprof_perf_event_stream_enable (SysprofPerfEventStream *self, self->active = TRUE; - g_source_modify_unix_fd (self->source, self->perf_fd_tag, G_IO_IN); - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ACTIVE]); return TRUE; @@ -536,8 +568,6 @@ sysprof_perf_event_stream_disable (SysprofPerfEventStream *self, self->active = FALSE; - g_source_modify_unix_fd (self->source, self->perf_fd_tag, G_IO_ERR); - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ACTIVE]); return TRUE; From 849db07ad631124d3355c7f2852b31f832d76f2b Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 5 Jun 2023 15:03:55 -0700 Subject: [PATCH 0346/1030] libsysprof-profile: stash kallsyms from Perf instrument That is the instrument that really provides things within the kernel address space, so it should be the instrument which copies the contents of kallsyms to the capture file (compressed). --- .../sysprof-linux-instrument.c | 3 +-- src/libsysprof-profile/sysprof-sampler.c | 26 +++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/libsysprof-profile/sysprof-linux-instrument.c b/src/libsysprof-profile/sysprof-linux-instrument.c index ceeffdc9..b9dd09e1 100644 --- a/src/libsysprof-profile/sysprof-linux-instrument.c +++ b/src/libsysprof-profile/sysprof-linux-instrument.c @@ -272,8 +272,7 @@ sysprof_linux_instrument_prepare_fiber (gpointer user_data) /* First get some basic information about the system into the capture. We can * get the contents for all of these concurrently. */ - if (!dex_await (dex_future_all (_sysprof_recording_add_file (recording, "/proc/kallsyms", TRUE), - _sysprof_recording_add_file (recording, "/proc/cpuinfo", TRUE), + if (!dex_await (dex_future_all (_sysprof_recording_add_file (recording, "/proc/cpuinfo", TRUE), _sysprof_recording_add_file (recording, "/proc/mounts", TRUE), NULL), &error)) diff --git a/src/libsysprof-profile/sysprof-sampler.c b/src/libsysprof-profile/sysprof-sampler.c index 530ecd80..17c0c994 100644 --- a/src/libsysprof-profile/sysprof-sampler.c +++ b/src/libsysprof-profile/sysprof-sampler.c @@ -219,6 +219,24 @@ sysprof_sampler_prepare_fiber (gpointer user_data) g_assert (SYSPROF_IS_RECORDING (prepare->recording)); g_assert (SYSPROF_IS_SAMPLER (prepare->sampler)); + /* First thing we need to do is to ensure the consumer has + * access to kallsyms, which may be from a machine, or boot + * different than this boot (and therefore symbols exist in + * different locations). Embed the kallsyms, but gzip it as + * those files can be quite large. + */ + dex_await (_sysprof_recording_add_file (recording, "/proc/kallsyms", TRUE), NULL); + + /* Now create a SysprofPerfEventStream for every CPU on the + * system. Linux Perf will only let us create a stream for + * a single PID on all CPU, or all PID on a single CPU. So + * we create one per-CPU and stream those results into the + * capture file during recording. + * + * Previously, we supported recording a single process but + * that is more effort than it is worth, since virtually + * nobody uses Sysprof that way. + */ n_cpu = g_get_num_processors (); futures = g_ptr_array_new_with_free_func (dex_unref); writer = _sysprof_recording_writer (prepare->recording); @@ -250,6 +268,9 @@ sysprof_sampler_prepare_fiber (gpointer user_data) if (!(connection = dex_await_object (dex_bus_get (G_BUS_TYPE_SYSTEM), &error))) return dex_future_new_for_error (g_steal_pointer (&error)); + /* Pipeline our request for n_cpu perf_event_open calls and then + * await them all to complete. + */ for (guint i = 0; i < n_cpu; i++) g_ptr_array_add (futures, sysprof_perf_event_stream_new (connection, @@ -264,6 +285,11 @@ sysprof_sampler_prepare_fiber (gpointer user_data) if (!dex_await (dex_future_allv ((DexFuture **)futures->pdata, futures->len), &error)) return dex_future_new_for_error (g_steal_pointer (&error)); + /* Save each of the streams (currently corked), so that we can + * uncork them while recording. We already checked that all the + * futures have succeeded above, so dex_await_object() must + * always return an object for each sub-future. + */ for (guint i = 0; i < futures->len; i++) { DexFuture *future = g_ptr_array_index (futures, i); From 745967253c49e441d509696a96fb264fd7830db9 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 5 Jun 2023 15:05:21 -0700 Subject: [PATCH 0347/1030] libsysprof-profile: copy/paste fix --- src/libsysprof-profile/sysprof-sampler.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libsysprof-profile/sysprof-sampler.c b/src/libsysprof-profile/sysprof-sampler.c index 17c0c994..b44940aa 100644 --- a/src/libsysprof-profile/sysprof-sampler.c +++ b/src/libsysprof-profile/sysprof-sampler.c @@ -225,7 +225,7 @@ sysprof_sampler_prepare_fiber (gpointer user_data) * different locations). Embed the kallsyms, but gzip it as * those files can be quite large. */ - dex_await (_sysprof_recording_add_file (recording, "/proc/kallsyms", TRUE), NULL); + dex_await (_sysprof_recording_add_file (prepare->recording, "/proc/kallsyms", TRUE), NULL); /* Now create a SysprofPerfEventStream for every CPU on the * system. Linux Perf will only let us create a stream for From 06a2a1a211944209b510f30090a35f0325bbc0e1 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 5 Jun 2023 15:15:35 -0700 Subject: [PATCH 0348/1030] tools: test expand/collapse with GtkEventControllerKey This ensures that we can maintain our expand/collapse keybindings that we have with GtkTreeView using GtkListView. --- src/tools/callgraph.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/tools/callgraph.c b/src/tools/callgraph.c index 91ac2d51..cc90a1b7 100644 --- a/src/tools/callgraph.c +++ b/src/tools/callgraph.c @@ -38,11 +38,36 @@ typedef struct _Augment guint32 total; } Augment; +static gboolean +on_key_pressed_cb (GtkEventControllerKey *key, + guint keyval, + guint keycode, + GdkModifierType state, + GtkTreeExpander *expander) +{ + GtkTreeListRow *row = gtk_tree_expander_get_list_row (expander); + + if (keyval == GDK_KEY_Right) + { + gtk_tree_list_row_set_expanded (row, TRUE); + return TRUE; + } + + if (keyval == GDK_KEY_Left) + { + gtk_tree_list_row_set_expanded (row, FALSE); + return TRUE; + } + + return FALSE; +} + static void function_setup (GtkSignalListItemFactory *factory, GtkListItem *list_item, SysprofCallgraph *callgraph) { + GtkEventController *controller; GtkWidget *expander; GtkWidget *text; @@ -56,6 +81,13 @@ function_setup (GtkSignalListItemFactory *factory, NULL); gtk_tree_expander_set_child (GTK_TREE_EXPANDER (expander), text); gtk_list_item_set_child (list_item, expander); + + controller = gtk_event_controller_key_new (); + g_signal_connect (controller, + "key-pressed", + G_CALLBACK (on_key_pressed_cb), + expander); + gtk_widget_add_controller (GTK_WIDGET (expander), controller); } static void From 0c6fcea79ba0aefe2e70421a66e699631d8c75d1 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 5 Jun 2023 15:27:16 -0700 Subject: [PATCH 0349/1030] libsysprof-profile: avoid splitting string into lines We will always have either \n or \0 at the end, so we can just sscanf this without needing to split strings. The sscanf() is still our top offender though, and so we should probably look for other ways to parse this string than sscanf(). --- src/libsysprof-profile/sysprof-linux-instrument.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/libsysprof-profile/sysprof-linux-instrument.c b/src/libsysprof-profile/sysprof-linux-instrument.c index b9dd09e1..cc4a55fb 100644 --- a/src/libsysprof-profile/sysprof-linux-instrument.c +++ b/src/libsysprof-profile/sysprof-linux-instrument.c @@ -26,6 +26,8 @@ #include "sysprof-podman-private.h" #include "sysprof-recording-private.h" +#include "line-reader-private.h" + struct _SysprofLinuxInstrument { SysprofInstrument parent_instance; @@ -53,26 +55,28 @@ add_mmaps (SysprofRecording *recording, gboolean ignore_inode) { SysprofCaptureWriter *writer; - g_auto(GStrv) lines = NULL; + LineReader reader; + const char *line; + gsize line_len; g_assert (SYSPROF_IS_RECORDING (recording)); g_assert (mapsstr != NULL); g_assert (pid > 0); writer = _sysprof_recording_writer (recording); - lines = g_strsplit (mapsstr, "\n", 0); - for (guint i = 0; lines[i] != NULL; i++) + line_reader_init (&reader, (char *)mapsstr, -1); + while ((line = line_reader_next (&reader, &line_len))) { char file[512]; gulong start; gulong end; gulong offset; gulong inode; - int r; gboolean is_vdso; + int r; - r = sscanf (lines[i], + r = sscanf (line, "%lx-%lx %*15s %lx %*x:%*x %lu %511[^\n]", &start, &end, &offset, &inode, file); file [sizeof file - 1] = '\0'; From 3de9f39348a41ca1dc6fb72b6d41918e4bd67c4d Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 5 Jun 2023 16:32:08 -0700 Subject: [PATCH 0350/1030] libsysprof-profile: parse address ranges with GRegex This uses G_REGEX_OPTIMIZE as well to get the benefit of the JIT when using PCRE2 via GLib. This turns out to take about half (or less) of the time that it takes to use sscanf() to parse the template/input string. 0.03 vs 0.08 seconds on my system. If someone wants to take this further, and just look at the input string rather than rely on GRegex, that'd be fine by me too. --- .../sysprof-linux-instrument.c | 90 ++++++++++++------- 1 file changed, 60 insertions(+), 30 deletions(-) diff --git a/src/libsysprof-profile/sysprof-linux-instrument.c b/src/libsysprof-profile/sysprof-linux-instrument.c index cc4a55fb..f43b56b7 100644 --- a/src/libsysprof-profile/sysprof-linux-instrument.c +++ b/src/libsysprof-profile/sysprof-linux-instrument.c @@ -40,6 +40,8 @@ enum { G_DEFINE_FINAL_TYPE (SysprofLinuxInstrument, sysprof_linux_instrument, SYSPROF_TYPE_INSTRUMENT) +static GRegex *address_range_regex; + static char ** sysprof_linux_instrument_list_required_policy (SysprofInstrument *instrument) { @@ -68,43 +70,64 @@ add_mmaps (SysprofRecording *recording, line_reader_init (&reader, (char *)mapsstr, -1); while ((line = line_reader_next (&reader, &line_len))) { - char file[512]; - gulong start; - gulong end; - gulong offset; - gulong inode; - gboolean is_vdso; - int r; + g_autoptr(GMatchInfo) match_info = NULL; - r = sscanf (line, - "%lx-%lx %*15s %lx %*x:%*x %lu %511[^\n]", - &start, &end, &offset, &inode, file); - file [sizeof file - 1] = '\0'; + if (g_regex_match_full (address_range_regex, line, line_len, 0, 0, &match_info, NULL)) + { + g_autofree char *file = NULL; + guint64 begin_addr; + guint64 end_addr; + guint64 inode; + guint64 offset; + gboolean is_vdso; + int begin_addr_begin; + int begin_addr_end; + int end_addr_begin; + int end_addr_end; + int offset_begin; + int offset_end; + int inode_begin; + int inode_end; + int path_begin; + int path_end; - /* file has a " (deleted)" suffix if it was deleted from disk */ - if (g_str_has_suffix (file, " (deleted)")) - file [strlen (file) - strlen (" (deleted)")] = '\0'; + if (!g_match_info_fetch_pos (match_info, 1, &begin_addr_begin, &begin_addr_end) || + !g_match_info_fetch_pos (match_info, 2, &end_addr_begin, &end_addr_end) || + !g_match_info_fetch_pos (match_info, 3, &offset_begin, &offset_end) || + !g_match_info_fetch_pos (match_info, 4, &inode_begin, &inode_end) || + !g_match_info_fetch_pos (match_info, 5, &path_begin, &path_end)) + continue; - if (r != 5) - continue; + begin_addr = g_ascii_strtoull (&line[begin_addr_begin], NULL, 16); + end_addr = g_ascii_strtoull (&line[end_addr_begin], NULL, 16); + offset = g_ascii_strtoull (&line[offset_begin], NULL, 16); + inode = g_ascii_strtoull (&line[inode_begin], NULL, 10); - is_vdso = strcmp ("[vdso]", file) == 0; + if (memcmp (" (deleted", + &line[path_end] - strlen (" (deleted"), + strlen (" (deleted")) == 0) + path_end -= strlen (" (deleted)"); - if (ignore_inode || is_vdso) - inode = 0; + file = g_strndup (&line[path_begin], path_end-path_begin); - if (is_vdso) - offset = 0; + is_vdso = strcmp ("[vdso]", file) == 0; - sysprof_capture_writer_add_map (writer, - SYSPROF_CAPTURE_CURRENT_TIME, - -1, - pid, - start, - end, - offset, - inode, - file); + if (ignore_inode || is_vdso) + inode = 0; + + if (is_vdso) + offset = 0; + + sysprof_capture_writer_add_map (writer, + SYSPROF_CAPTURE_CURRENT_TIME, + -1, + pid, + begin_addr, + end_addr, + offset, + inode, + file); + } } } @@ -379,6 +402,7 @@ sysprof_linux_instrument_class_init (SysprofLinuxInstrumentClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); SysprofInstrumentClass *instrument_class = SYSPROF_INSTRUMENT_CLASS (klass); + g_autoptr(GError) error = NULL; object_class->finalize = sysprof_linux_instrument_finalize; @@ -386,6 +410,12 @@ sysprof_linux_instrument_class_init (SysprofLinuxInstrumentClass *klass) instrument_class->prepare = sysprof_linux_instrument_prepare; instrument_class->record = sysprof_linux_instrument_record; instrument_class->process_started = sysprof_linux_instrument_process_started; + + address_range_regex = g_regex_new ("^([0-9a-f]+)-([0-9a-f]+) [r\\-][w\\-][x\\-][ps\\-] [0-9a-f]+ [0-9]{2}:[0-9]{2} ([0-9]+) +(.*)$", + G_REGEX_OPTIMIZE, + G_REGEX_MATCH_DEFAULT, + &error); + g_assert_no_error (error); } static void From 9cba99d894ff79ed02c3343e436bb26a35d4aa26 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 5 Jun 2023 16:45:07 -0700 Subject: [PATCH 0351/1030] libsysprof-analyze: handle pid == -1 This can happen from Perf sometimes so be explicit about remapping it to process 0, which is our sort of catchall for kernel stuff. --- src/libsysprof-analyze/sysprof-document.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index bdefa231..4d3b6d2a 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -1246,6 +1246,9 @@ _sysprof_document_process_symbol (SysprofDocument *self, g_return_val_if_fail (SYSPROF_IS_DOCUMENT (self), NULL); + if (pid < 0) + pid = 0; + info = _sysprof_document_process_info (self, pid, TRUE); if (info->symbol) From 3fb1a15eecc17d5b900ac1cff9a54df53f55cc70 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 5 Jun 2023 16:56:18 -0700 Subject: [PATCH 0352/1030] tools: toggle row on space --- src/tools/callgraph.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/tools/callgraph.c b/src/tools/callgraph.c index cc90a1b7..da67c019 100644 --- a/src/tools/callgraph.c +++ b/src/tools/callgraph.c @@ -47,19 +47,16 @@ on_key_pressed_cb (GtkEventControllerKey *key, { GtkTreeListRow *row = gtk_tree_expander_get_list_row (expander); - if (keyval == GDK_KEY_Right) - { - gtk_tree_list_row_set_expanded (row, TRUE); - return TRUE; - } + if (keyval == GDK_KEY_space) + gtk_tree_list_row_set_expanded (row, !gtk_tree_list_row_get_expanded (row)); + else if (keyval == GDK_KEY_Right) + gtk_tree_list_row_set_expanded (row, TRUE); + else if (keyval == GDK_KEY_Left) + gtk_tree_list_row_set_expanded (row, FALSE); + else + return FALSE; - if (keyval == GDK_KEY_Left) - { - gtk_tree_list_row_set_expanded (row, FALSE); - return TRUE; - } - - return FALSE; + return TRUE; } static void From 200b8acb2047bdfa04d0ebd27a78a61c8ae8ca03 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 6 Jun 2023 11:19:03 -0700 Subject: [PATCH 0353/1030] libsysprof-profile: add scaffolding for battery charge This is a SysprofInstrument that will eventually contain the contents of the sysprof-battery-source.c to get battery charge/rate/etc. Note that this is different from energy usage which will come from RAPL. --- src/libsysprof-profile/meson.build | 2 + .../sysprof-battery-charge.c | 121 ++++++++++++++++++ .../sysprof-battery-charge.h | 42 ++++++ src/libsysprof-profile/sysprof-profile.h | 1 + src/libsysprof-profile/tests/test-profiler.c | 1 + 5 files changed, 167 insertions(+) create mode 100644 src/libsysprof-profile/sysprof-battery-charge.c create mode 100644 src/libsysprof-profile/sysprof-battery-charge.h diff --git a/src/libsysprof-profile/meson.build b/src/libsysprof-profile/meson.build index 340cefd5..9d31c715 100644 --- a/src/libsysprof-profile/meson.build +++ b/src/libsysprof-profile/meson.build @@ -1,4 +1,5 @@ libsysprof_profile_public_sources = [ + 'sysprof-battery-charge.c', 'sysprof-cpu-usage.c', 'sysprof-disk-usage.c', 'sysprof-instrument.c', @@ -21,6 +22,7 @@ libsysprof_profile_private_sources = [ libsysprof_profile_public_headers = [ 'sysprof-profile.h', + 'sysprof-battery-charge.h', 'sysprof-cpu-usage.h', 'sysprof-disk-usage.h', 'sysprof-instrument.h', diff --git a/src/libsysprof-profile/sysprof-battery-charge.c b/src/libsysprof-profile/sysprof-battery-charge.c new file mode 100644 index 00000000..18e858b4 --- /dev/null +++ b/src/libsysprof-profile/sysprof-battery-charge.c @@ -0,0 +1,121 @@ +/* sysprof-battery-charge.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "line-reader-private.h" + +#include "sysprof-battery-charge.h" +#include "sysprof-instrument-private.h" +#include "sysprof-recording-private.h" + +struct _SysprofBatteryCharge +{ + SysprofInstrument parent_instance; +}; + +struct _SysprofBatteryChargeClass +{ + SysprofInstrumentClass parent_class; +}; + +G_DEFINE_FINAL_TYPE (SysprofBatteryCharge, sysprof_battery_charge, SYSPROF_TYPE_INSTRUMENT) + +typedef struct _Record +{ + SysprofRecording *recording; + DexFuture *cancellable; +} Record; + +static void +record_free (gpointer data) +{ + Record *record = data; + + g_clear_object (&record->recording); + dex_clear (&record->cancellable); + g_free (record); +} + +static DexFuture * +sysprof_battery_charge_record_fiber (gpointer user_data) +{ + G_GNUC_UNUSED SysprofCaptureWriter *writer; + Record *record = user_data; + + g_assert (record != NULL); + g_assert (SYSPROF_IS_RECORDING (record->recording)); + g_assert (DEX_IS_FUTURE (record->cancellable)); + + writer = _sysprof_recording_writer (record->recording); + + return dex_future_new_for_boolean (TRUE); +} + +static DexFuture * +sysprof_battery_charge_record (SysprofInstrument *instrument, + SysprofRecording *recording, + GCancellable *cancellable) +{ + Record *record; + + g_assert (SYSPROF_IS_BATTERY_CHARGE (instrument)); + g_assert (SYSPROF_IS_RECORDING (recording)); + g_assert (G_IS_CANCELLABLE (cancellable)); + + record = g_new0 (Record, 1); + record->recording = g_object_ref (recording); + record->cancellable = dex_cancellable_new_from_cancellable (cancellable); + + return dex_scheduler_spawn (NULL, 0, + sysprof_battery_charge_record_fiber, + record, + record_free); +} + +static void +sysprof_battery_charge_class_init (SysprofBatteryChargeClass *klass) +{ + SysprofInstrumentClass *instrument_class = SYSPROF_INSTRUMENT_CLASS (klass); + + instrument_class->record = sysprof_battery_charge_record; +} + +static void +sysprof_battery_charge_init (SysprofBatteryCharge *self) +{ +} + +SysprofInstrument * +sysprof_battery_charge_new (void) +{ + return g_object_new (SYSPROF_TYPE_BATTERY_CHARGE, NULL); +} diff --git a/src/libsysprof-profile/sysprof-battery-charge.h b/src/libsysprof-profile/sysprof-battery-charge.h new file mode 100644 index 00000000..1d3093d1 --- /dev/null +++ b/src/libsysprof-profile/sysprof-battery-charge.h @@ -0,0 +1,42 @@ +/* sysprof-battery-charge.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-instrument.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_BATTERY_CHARGE (sysprof_battery_charge_get_type()) +#define SYSPROF_IS_BATTERY_CHARGE(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_BATTERY_CHARGE) +#define SYSPROF_BATTERY_CHARGE(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_BATTERY_CHARGE, SysprofBatteryCharge) +#define SYSPROF_BATTERY_CHARGE_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_BATTERY_CHARGE, SysprofBatteryChargeClass) + +typedef struct _SysprofBatteryCharge SysprofBatteryCharge; +typedef struct _SysprofBatteryChargeClass SysprofBatteryChargeClass; + +SYSPROF_AVAILABLE_IN_ALL +GType sysprof_battery_charge_get_type (void) G_GNUC_CONST; +SYSPROF_AVAILABLE_IN_ALL +SysprofInstrument *sysprof_battery_charge_new (void); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofBatteryCharge, g_object_unref) + +G_END_DECLS diff --git a/src/libsysprof-profile/sysprof-profile.h b/src/libsysprof-profile/sysprof-profile.h index 8ad775e2..cdc527df 100644 --- a/src/libsysprof-profile/sysprof-profile.h +++ b/src/libsysprof-profile/sysprof-profile.h @@ -25,6 +25,7 @@ G_BEGIN_DECLS #define SYSPROF_PROFILE_INSIDE +# include "sysprof-battery-charge.h" # include "sysprof-cpu-usage.h" # include "sysprof-disk-usage.h" # include "sysprof-instrument.h" diff --git a/src/libsysprof-profile/tests/test-profiler.c b/src/libsysprof-profile/tests/test-profiler.c index a57adbee..365a09d4 100644 --- a/src/libsysprof-profile/tests/test-profiler.c +++ b/src/libsysprof-profile/tests/test-profiler.c @@ -116,6 +116,7 @@ main (int argc, profiler = sysprof_profiler_new (); + sysprof_profiler_add_instrument (profiler, sysprof_battery_charge_new ()); sysprof_profiler_add_instrument (profiler, sysprof_cpu_usage_new ()); sysprof_profiler_add_instrument (profiler, sysprof_disk_usage_new ()); sysprof_profiler_add_instrument (profiler, sysprof_memory_usage_new ()); From f1826bfc86a7b94d7c6bbd6c839656d6cbabe0da Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 6 Jun 2023 12:29:01 -0700 Subject: [PATCH 0354/1030] libsysprof-profile: implement battery charge counters This parses the battery charge values on a regular interval and exits when the recording is stopped. --- .../sysprof-battery-charge.c | 187 +++++++++++++++++- 1 file changed, 186 insertions(+), 1 deletion(-) diff --git a/src/libsysprof-profile/sysprof-battery-charge.c b/src/libsysprof-profile/sysprof-battery-charge.c index 18e858b4..4ad534f3 100644 --- a/src/libsysprof-profile/sysprof-battery-charge.c +++ b/src/libsysprof-profile/sysprof-battery-charge.c @@ -37,6 +37,8 @@ #include "sysprof-instrument-private.h" #include "sysprof-recording-private.h" +#define SYS_CLASS_POWER_SUPPLY "/sys/class/power_supply/" + struct _SysprofBatteryCharge { SysprofInstrument parent_instance; @@ -65,17 +67,200 @@ record_free (gpointer data) g_free (record); } +static void +clear_fd (gpointer ptr) +{ + int *fdptr = ptr; + g_clear_fd (fdptr, NULL); +} + +static char ** +collect_battery_names (void) +{ + GPtrArray *ar = g_ptr_array_new (); + g_autoptr(GDir) dir = NULL; + + if ((dir = g_dir_open (SYS_CLASS_POWER_SUPPLY, 0, NULL))) + { + const char *name; + + while ((name = g_dir_read_name (dir))) + { + if (strcmp (name, "AC") == 0) + continue; + + g_ptr_array_add (ar, g_strdup (name)); + } + } + + g_ptr_array_add (ar, NULL); + + return (char **)g_ptr_array_free (ar, FALSE); +} + +typedef struct _ReadBuffer +{ + char buf[32]; +} ReadBuffer; + static DexFuture * sysprof_battery_charge_record_fiber (gpointer user_data) { - G_GNUC_UNUSED SysprofCaptureWriter *writer; + const int invalid_fd = -1; + SysprofCaptureCounterValue *values; + SysprofCaptureCounter *counters; + SysprofCaptureWriter *writer; Record *record = user_data; + g_autoptr(GArray) charge_fds = NULL; + g_auto(GStrv) names = NULL; + ReadBuffer *bufs; + guint *ids; + guint n_names; + guint n_counters = 1; g_assert (record != NULL); g_assert (SYSPROF_IS_RECORDING (record->recording)); g_assert (DEX_IS_FUTURE (record->cancellable)); writer = _sysprof_recording_writer (record->recording); + names = collect_battery_names (); + n_names = g_strv_length (names); + + /* Use some stack space for our counters and values. */ + ids = g_alloca0 (sizeof *ids * (n_names + 1)); + counters = g_alloca0 (sizeof *counters * (n_names + 1)); + values = g_alloca0 (sizeof *values * (n_names + 1)); + bufs = g_alloca0 (sizeof (ReadBuffer) * (n_names + 1)); + + /* Setup the combined counter which is the total charge of all of + * the batteries we discover on the system. + */ + counters[0].id = ids[0] = sysprof_capture_writer_request_counter (writer, 1); + g_strlcpy (counters[0].category, "Battery Charge", sizeof counters[0].category); + g_strlcpy (counters[0].name, "Combined", sizeof counters[0].name); + g_snprintf (counters[0].description, sizeof counters[0].description, "Combined Battery Charge (µAh)"); + counters[0].type = SYSPROF_CAPTURE_COUNTER_INT64; + counters[0].value.v64 = 0; + + /* Create array to store open FDs to the charge file. We'll do a + * positioned read at 0 on these so we get new data on each subsequent + * read via AIO. Set the first FD to invalid since that will map to the + * position of the combined-counter. + */ + charge_fds = g_array_new (FALSE, FALSE, sizeof (int)); + g_array_set_clear_func (charge_fds, clear_fd); + g_array_append_val (charge_fds, invalid_fd); + + for (guint i = 0; names[i]; i++) + { + SysprofCaptureCounter *counter = &counters[n_counters]; + const char *name = names[i]; + g_autofree char *charge_path = g_build_filename (SYS_CLASS_POWER_SUPPLY, name, "charge_now", NULL); + g_autofree char *model_name_path = g_build_filename (SYS_CLASS_POWER_SUPPLY, name, "model_name", NULL); + g_autofree char *type_path = g_build_filename (SYS_CLASS_POWER_SUPPLY, name, "type", NULL); + g_autofree char *model_name_data = NULL; + g_autofree char *type_data = NULL; + g_autofd int charge_fd = -1; + + if (!g_file_get_contents (type_path, &type_data, NULL, NULL) || + !g_str_has_prefix (type_data, "Battery") || + -1 == (charge_fd = open (charge_path, O_RDONLY|O_CLOEXEC))) + continue; + + counter->id = ids[n_counters] = sysprof_capture_writer_request_counter (writer, 1); + counter->type = SYSPROF_CAPTURE_COUNTER_INT64; + g_strlcpy (counter->category, "Battery Charge", sizeof counter->description); + if (g_file_get_contents (model_name_path, &model_name_data, NULL, NULL)) + g_strlcpy (counter->name, g_strstrip (model_name_data), sizeof counter->name); + else + g_strlcpy (counter->name, name, sizeof counter->name); + g_snprintf (counter->description, sizeof counter->description, "%s (µAh)", counter->name); + counter->value.v64 = 0; + + g_array_append_val (charge_fds, charge_fd); + charge_fd = -1; + + n_counters++; + } + + /* If we only have the combined counter, then just short-circuit and + * don't record any counters. Otherwise the battery charge row might + * show up in UI which we would want to omit. + */ + if (n_counters == 1) + return dex_future_new_for_boolean (TRUE); + + sysprof_capture_writer_define_counters (writer, + SYSPROF_CAPTURE_CURRENT_TIME, + -1, + -1, + counters, + n_counters); + + /* Main recording loop */ + for (;;) + { + g_autoptr(GPtrArray) futures = g_ptr_array_new_with_free_func (dex_unref); + + /* Read the contents of all the charge buffers in a single + * submission to the AIO layer. + */ + g_ptr_array_add (futures, dex_future_new_for_boolean (TRUE)); + for (guint i = 1; i < charge_fds->len; i++) + { + int charge_fd = g_array_index (charge_fds, int, i); + + g_ptr_array_add (futures, + dex_aio_read (NULL, + charge_fd, + &bufs[i].buf, + sizeof bufs[i].buf-1, + 0)); + } + + /* Now wait until all the reads complete */ + if (futures->len > 0) + dex_await (dex_future_anyv ((DexFuture **)futures->pdata, futures->len), NULL); + + /* Parse the current charge values */ + values[0].v64 = 0; + for (guint i = 1; i < charge_fds->len; i++) + { + gssize len = dex_await_int64 (dex_ref (g_ptr_array_index (futures, i)), NULL); + guint64 v64; + + if (len <= 0) + continue; + + errno = 0; + bufs[i].buf[len] = 0; + v64 = g_ascii_strtoull (bufs[i].buf, NULL, 10); + if (v64 == G_MAXUINT64 || errno != 0) + continue; + + values[i].v64 = v64; + values[0].v64 += v64; + } + + /* Deliver new values to capture */ + sysprof_capture_writer_set_counters (writer, + SYSPROF_CAPTURE_CURRENT_TIME, + -1, + -1, + ids, + values, + n_counters); + + /* Wait for cancellation or ½ second */ + dex_await (dex_future_first (dex_ref (record->cancellable), + dex_timeout_new_usec (G_USEC_PER_SEC / 2), + NULL), + NULL); + + /* If cancellable is rejected, then we're done recording */ + if (dex_future_get_status (record->cancellable) != DEX_FUTURE_STATUS_PENDING) + break; + } return dex_future_new_for_boolean (TRUE); } From b38850f27d72a4fe2ef23829890576326c5d1fd9 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 6 Jun 2023 15:35:43 -0700 Subject: [PATCH 0355/1030] libsysprof-profile: ensure instruments finish recording They may still be doing operations that need to complete before we can move on to the augmentation phase. --- src/libsysprof-profile/sysprof-recording.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/libsysprof-profile/sysprof-recording.c b/src/libsysprof-profile/sysprof-recording.c index 25e1d35c..43fb45bf 100644 --- a/src/libsysprof-profile/sysprof-recording.c +++ b/src/libsysprof-profile/sysprof-recording.c @@ -162,6 +162,12 @@ stop_recording: */ g_cancellable_cancel (cancellable); + /* But we must still wait for instruments to respond to + * the cancellation and clean up before we can move onto + * the augmentation phase. + */ + dex_await (dex_ref (record), NULL); + /* Let instruments augment the capture. Some instruments may include * extra information about the capture such as symbol names and their * address ranges per-process. From 6f661c6bfe7f74695e0e674530c99a1b8dda4b49 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 6 Jun 2023 15:55:49 -0700 Subject: [PATCH 0356/1030] libsysprof-profile: add proxied instrument This fetches data into a memfd from a peer process and then appends it to the capture at the end of recording. --- src/libsysprof-profile/meson.build | 2 + src/libsysprof-profile/sysprof-profile.h | 1 + .../sysprof-proxied-instrument-private.h | 42 +++ .../sysprof-proxied-instrument.c | 310 ++++++++++++++++++ .../sysprof-proxied-instrument.h | 46 +++ 5 files changed, 401 insertions(+) create mode 100644 src/libsysprof-profile/sysprof-proxied-instrument-private.h create mode 100644 src/libsysprof-profile/sysprof-proxied-instrument.c create mode 100644 src/libsysprof-profile/sysprof-proxied-instrument.h diff --git a/src/libsysprof-profile/meson.build b/src/libsysprof-profile/meson.build index 9d31c715..56893e7b 100644 --- a/src/libsysprof-profile/meson.build +++ b/src/libsysprof-profile/meson.build @@ -6,6 +6,7 @@ libsysprof_profile_public_sources = [ 'sysprof-memory-usage.c', 'sysprof-network-usage.c', 'sysprof-profiler.c', + 'sysprof-proxied-instrument.c', 'sysprof-recording.c', 'sysprof-sampler.c', 'sysprof-spawnable.c', @@ -29,6 +30,7 @@ libsysprof_profile_public_headers = [ 'sysprof-memory-usage.h', 'sysprof-network-usage.h', 'sysprof-profiler.h', + 'sysprof-proxied-instrument.h', 'sysprof-recording.h', 'sysprof-sampler.h', 'sysprof-spawnable.h', diff --git a/src/libsysprof-profile/sysprof-profile.h b/src/libsysprof-profile/sysprof-profile.h index cdc527df..1c3b9a05 100644 --- a/src/libsysprof-profile/sysprof-profile.h +++ b/src/libsysprof-profile/sysprof-profile.h @@ -32,6 +32,7 @@ G_BEGIN_DECLS # include "sysprof-memory-usage.h" # include "sysprof-network-usage.h" # include "sysprof-profiler.h" +# include "sysprof-proxied-instrument.h" # include "sysprof-recording.h" # include "sysprof-sampler.h" # include "sysprof-spawnable.h" diff --git a/src/libsysprof-profile/sysprof-proxied-instrument-private.h b/src/libsysprof-profile/sysprof-proxied-instrument-private.h new file mode 100644 index 00000000..327e03d2 --- /dev/null +++ b/src/libsysprof-profile/sysprof-proxied-instrument-private.h @@ -0,0 +1,42 @@ +/* sysprof-proxied-instrument-private.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-instrument-private.h" +#include "sysprof-proxied-instrument.h" + +G_BEGIN_DECLS + +struct _SysprofProxiedInstrument +{ + SysprofInstrument parent_instance; + GBusType bus_type; + char *bus_name; + char *object_path; + guint call_stop_first : 1; +}; + +struct _SysprofProxiedInstrumentClass +{ + SysprofInstrumentClass parent_class; +}; + +G_END_DECLS diff --git a/src/libsysprof-profile/sysprof-proxied-instrument.c b/src/libsysprof-profile/sysprof-proxied-instrument.c new file mode 100644 index 00000000..9e01541a --- /dev/null +++ b/src/libsysprof-profile/sysprof-proxied-instrument.c @@ -0,0 +1,310 @@ +/* sysprof-proxied-instrument.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include + +#include "sysprof-proxied-instrument-private.h" +#include "sysprof-recording-private.h" + + +enum { + PROP_0, + PROP_BUS_TYPE, + PROP_BUS_NAME, + PROP_OBJECT_PATH, + N_PROPS +}; + +G_DEFINE_TYPE (SysprofProxiedInstrument, sysprof_proxied_instrument, SYSPROF_TYPE_INSTRUMENT) + +static GParamSpec *properties [N_PROPS]; + +typedef struct _Record +{ + SysprofRecording *recording; + DexFuture *cancellable; + char *bus_name; + char *object_path; + GBusType bus_type; + guint call_stop_first : 1; +} Record; + +static void +record_free (Record *record) +{ + g_clear_object (&record->recording); + dex_clear (&record->cancellable); + g_clear_pointer (&record->bus_name, g_free); + g_clear_pointer (&record->object_path, g_free); + g_free (record); +} + +static DexFuture * +sysprof_proxied_instrument_record_fiber (gpointer user_data) +{ + Record *record = user_data; + SysprofCaptureWriter *writer; + SysprofCaptureReader *reader; + g_autoptr(GDBusConnection) connection = NULL; + g_autoptr(GUnixFDList) fd_list = NULL; + g_autoptr(DexFuture) started = NULL; + g_autoptr(GError) error = NULL; + GVariantDict options = G_VARIANT_DICT_INIT (NULL); + g_autofd int proxy_fd = -1; + int handle; + + g_assert (record != NULL); + g_assert (record->bus_type != G_BUS_TYPE_NONE); + g_assert (record->bus_name != NULL); + g_assert (record->object_path != NULL); + g_assert (SYSPROF_IS_RECORDING (record->recording)); + g_assert (DEX_IS_CANCELLABLE (record->cancellable)); + + /* Wait for our connection to be available */ + if (!(connection = dex_await_object (dex_bus_get (record->bus_type), &error))) + return dex_future_new_for_error (g_steal_pointer (&error)); + + /* If we should try calling stop first to cancel any in-flight + * recording. We could pipeline this, but if the other side + * handles messages on threads, they would race. + */ + if (record->call_stop_first) + dex_await (dex_dbus_connection_call (connection, + record->bus_name, + record->object_path, + "org.gnome.Sysprof3.Profiler", + "Stop", + g_variant_new ("()"), + G_VARIANT_TYPE ("()"), + G_DBUS_CALL_FLAGS_NONE, + -1), + NULL); + + /* Create a new FD that the peer will be able to write to and + * we will concatenate into the capture after recording. + */ + if (-1 == (proxy_fd = sysprof_memfd_create ("[sysprof-proxy]"))) + return dex_future_new_for_errno (errno); + + /* Create FDList to pass to the peer so they can get our FD */ + fd_list = g_unix_fd_list_new (); + if (-1 == (handle = g_unix_fd_list_append (fd_list, proxy_fd, &error))) + return dex_future_new_for_error (g_steal_pointer (&error)); + + /* Call the proxy and give it our FD to start recording */ + if (!dex_await (dex_dbus_connection_call_with_unix_fd_list (connection, + record->bus_name, + record->object_path, + "org.gnome.Sysprof3.Profiler", + "Start", + g_variant_new ("(@a{sv}h)", + g_variant_dict_end (&options), + handle), + G_VARIANT_TYPE ("()"), + G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION, + G_MAXUINT, + fd_list), + &error)) + { + g_debug ("Failed to start profiler at %s %s: %s", + record->bus_name, + record->object_path, + error->message); + return dex_future_new_for_error (g_steal_pointer (&error)); + } + + /* Await completion of recording */ + dex_await (dex_ref (record->cancellable), NULL); + + /* Call the proxy and let them know to stop */ + dex_await (dex_dbus_connection_call (connection, + record->bus_name, + record->object_path, + "org.gnome.Sysprof3.Profiler", + "Stop", + g_variant_new ("()"), + G_VARIANT_TYPE ("()"), + G_DBUS_CALL_FLAGS_NONE, + -1), + &error); + + if (error != NULL) + g_warning ("Failed to stop profiler at %s %s: %s", + record->bus_name, + record->object_path, + error->message); + + /* Reset the file position to the start */ + lseek (proxy_fd, 0, SEEK_SET); + + /* Now cat the recording into our writer */ + writer = _sysprof_recording_writer (record->recording); + reader = sysprof_capture_reader_new_from_fd (g_steal_fd (&proxy_fd)); + sysprof_capture_writer_cat (writer, reader); + sysprof_capture_reader_unref (reader); + + return dex_future_new_for_boolean (TRUE); +} + +static DexFuture * +sysprof_proxied_instrument_record (SysprofInstrument *instrument, + SysprofRecording *recording, + GCancellable *cancellable) +{ + SysprofProxiedInstrument *self = (SysprofProxiedInstrument *)instrument; + Record *record; + + g_assert (SYSPROF_IS_PROXIED_INSTRUMENT (self)); + g_assert (SYSPROF_IS_RECORDING (recording)); + g_assert (G_IS_CANCELLABLE (cancellable)); + + record = g_new0 (Record, 1); + record->recording = g_object_ref (recording); + record->cancellable = dex_cancellable_new_from_cancellable (cancellable); + record->bus_name = g_strdup (self->bus_name); + record->object_path = g_strdup (self->object_path); + record->bus_type = self->bus_type; + record->call_stop_first = self->call_stop_first; + + return dex_scheduler_spawn (NULL, 0, + sysprof_proxied_instrument_record_fiber, + record, + (GDestroyNotify)record_free); +} + +static void +sysprof_proxied_instrument_finalize (GObject *object) +{ + SysprofProxiedInstrument *self = (SysprofProxiedInstrument *)object; + + g_clear_pointer (&self->bus_name, g_free); + g_clear_pointer (&self->object_path, g_free); + + G_OBJECT_CLASS (sysprof_proxied_instrument_parent_class)->finalize (object); +} + +static void +sysprof_proxied_instrument_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofProxiedInstrument *self = SYSPROF_PROXIED_INSTRUMENT (object); + + switch (prop_id) + { + case PROP_BUS_TYPE: + g_value_set_enum (value, self->bus_type); + break; + + case PROP_BUS_NAME: + g_value_set_string (value, self->bus_name); + break; + + case PROP_OBJECT_PATH: + g_value_set_string (value, self->object_path); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_proxied_instrument_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SysprofProxiedInstrument *self = SYSPROF_PROXIED_INSTRUMENT (object); + + switch (prop_id) + { + case PROP_BUS_TYPE: + self->bus_type = g_value_get_enum (value); + break; + + case PROP_BUS_NAME: + self->bus_name = g_value_dup_string (value); + break; + + case PROP_OBJECT_PATH: + self->object_path = g_value_dup_string (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_proxied_instrument_class_init (SysprofProxiedInstrumentClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + SysprofInstrumentClass *instrument_class = SYSPROF_INSTRUMENT_CLASS (klass); + + object_class->finalize = sysprof_proxied_instrument_finalize; + object_class->get_property = sysprof_proxied_instrument_get_property; + object_class->set_property = sysprof_proxied_instrument_set_property; + + instrument_class->record = sysprof_proxied_instrument_record; + + properties [PROP_BUS_TYPE] = + g_param_spec_enum ("bus-type", NULL, NULL, + G_TYPE_BUS_TYPE, + G_BUS_TYPE_NONE, + (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + + properties [PROP_BUS_NAME] = + g_param_spec_string ("bus-name", NULL, NULL, + NULL, + (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + + properties [PROP_OBJECT_PATH] = + g_param_spec_string ("object-path", NULL, NULL, + NULL, + (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); +} + +static void +sysprof_proxied_instrument_init (SysprofProxiedInstrument *self) +{ +} + +SysprofInstrument * +sysprof_proxied_instrument_new (GBusType bus_type, + const char *bus_name, + const char *object_path) +{ + g_return_val_if_fail (bus_type == G_BUS_TYPE_SYSTEM || + bus_type == G_BUS_TYPE_SESSION, NULL); + g_return_val_if_fail (bus_name != NULL, NULL); + g_return_val_if_fail (object_path != NULL, NULL); + + return g_object_new (SYSPROF_TYPE_PROXIED_INSTRUMENT, + "bus-type", bus_type, + "bus-name", bus_name, + "object-path", object_path, + NULL); +} diff --git a/src/libsysprof-profile/sysprof-proxied-instrument.h b/src/libsysprof-profile/sysprof-proxied-instrument.h new file mode 100644 index 00000000..0af100fe --- /dev/null +++ b/src/libsysprof-profile/sysprof-proxied-instrument.h @@ -0,0 +1,46 @@ +/* sysprof-proxied-instrument.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +#include "sysprof-instrument.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_PROXIED_INSTRUMENT (sysprof_proxied_instrument_get_type()) +#define SYSPROF_IS_PROXIED_INSTRUMENT(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_PROXIED_INSTRUMENT) +#define SYSPROF_PROXIED_INSTRUMENT(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_PROXIED_INSTRUMENT, SysprofProxiedInstrument) +#define SYSPROF_PROXIED_INSTRUMENT_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_PROXIED_INSTRUMENT, SysprofProxiedInstrumentClass) + +typedef struct _SysprofProxiedInstrument SysprofProxiedInstrument; +typedef struct _SysprofProxiedInstrumentClass SysprofProxiedInstrumentClass; + +SYSPROF_AVAILABLE_IN_ALL +GType sysprof_proxied_instrument_get_type (void) G_GNUC_CONST; +SYSPROF_AVAILABLE_IN_ALL +SysprofInstrument *sysprof_proxied_instrument_new (GBusType bus_type, + const char *bus_name, + const char *object_path); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofProxiedInstrument, g_object_unref) + +G_END_DECLS From 0e625cea210d1727be63498d7d63c1ca08dd0dcd Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 6 Jun 2023 15:56:38 -0700 Subject: [PATCH 0357/1030] libsysprof-profile: add RAPL-based SysprofEnergyUsage This does what we did previously with RAPL via sysprofd, with the new SysprofInstrument API. --- src/libsysprof-profile/meson.build | 2 + src/libsysprof-profile/sysprof-energy-usage.c | 57 +++++++++++++++++++ src/libsysprof-profile/sysprof-energy-usage.h | 42 ++++++++++++++ src/libsysprof-profile/sysprof-profile.h | 1 + src/libsysprof-profile/tests/test-profiler.c | 1 + 5 files changed, 103 insertions(+) create mode 100644 src/libsysprof-profile/sysprof-energy-usage.c create mode 100644 src/libsysprof-profile/sysprof-energy-usage.h diff --git a/src/libsysprof-profile/meson.build b/src/libsysprof-profile/meson.build index 56893e7b..7277ab98 100644 --- a/src/libsysprof-profile/meson.build +++ b/src/libsysprof-profile/meson.build @@ -2,6 +2,7 @@ libsysprof_profile_public_sources = [ 'sysprof-battery-charge.c', 'sysprof-cpu-usage.c', 'sysprof-disk-usage.c', + 'sysprof-energy-usage.c', 'sysprof-instrument.c', 'sysprof-memory-usage.c', 'sysprof-network-usage.c', @@ -26,6 +27,7 @@ libsysprof_profile_public_headers = [ 'sysprof-battery-charge.h', 'sysprof-cpu-usage.h', 'sysprof-disk-usage.h', + 'sysprof-energy-usage.h', 'sysprof-instrument.h', 'sysprof-memory-usage.h', 'sysprof-network-usage.h', diff --git a/src/libsysprof-profile/sysprof-energy-usage.c b/src/libsysprof-profile/sysprof-energy-usage.c new file mode 100644 index 00000000..6c4db985 --- /dev/null +++ b/src/libsysprof-profile/sysprof-energy-usage.c @@ -0,0 +1,57 @@ +/* sysprof-energy-usage.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-proxied-instrument-private.h" +#include "sysprof-energy-usage.h" + +struct _SysprofEnergyUsage +{ + SysprofProxiedInstrument parent_instance; +}; + +struct _SysprofEnergyUsageClass +{ + SysprofProxiedInstrumentClass parent_class; +}; + +G_DEFINE_FINAL_TYPE (SysprofEnergyUsage, sysprof_energy_usage, SYSPROF_TYPE_PROXIED_INSTRUMENT) + +static void +sysprof_energy_usage_class_init (SysprofEnergyUsageClass *klass) +{ +} + +static void +sysprof_energy_usage_init (SysprofEnergyUsage *self) +{ + SYSPROF_PROXIED_INSTRUMENT (self)->call_stop_first = TRUE; +} + +SysprofInstrument * +sysprof_energy_usage_new (void) +{ + return g_object_new (SYSPROF_TYPE_ENERGY_USAGE, + "bus-type", G_BUS_TYPE_SYSTEM, + "bus-name", "org.gnome.Sysprof3", + "object-path", "/org/gnome/Sysprof3/RAPL", + NULL); +} diff --git a/src/libsysprof-profile/sysprof-energy-usage.h b/src/libsysprof-profile/sysprof-energy-usage.h new file mode 100644 index 00000000..211a2dfd --- /dev/null +++ b/src/libsysprof-profile/sysprof-energy-usage.h @@ -0,0 +1,42 @@ +/* sysprof-energy-usage.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-instrument.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_ENERGY_USAGE (sysprof_energy_usage_get_type()) +#define SYSPROF_IS_ENERGY_USAGE(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_ENERGY_USAGE) +#define SYSPROF_ENERGY_USAGE(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_ENERGY_USAGE, SysprofEnergyUsage) +#define SYSPROF_ENERGY_USAGE_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_ENERGY_USAGE, SysprofEnergyUsageClass) + +typedef struct _SysprofEnergyUsage SysprofEnergyUsage; +typedef struct _SysprofEnergyUsageClass SysprofEnergyUsageClass; + +SYSPROF_AVAILABLE_IN_ALL +GType sysprof_energy_usage_get_type (void) G_GNUC_CONST; +SYSPROF_AVAILABLE_IN_ALL +SysprofInstrument *sysprof_energy_usage_new (void); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofEnergyUsage, g_object_unref) + +G_END_DECLS diff --git a/src/libsysprof-profile/sysprof-profile.h b/src/libsysprof-profile/sysprof-profile.h index 1c3b9a05..067af039 100644 --- a/src/libsysprof-profile/sysprof-profile.h +++ b/src/libsysprof-profile/sysprof-profile.h @@ -28,6 +28,7 @@ G_BEGIN_DECLS # include "sysprof-battery-charge.h" # include "sysprof-cpu-usage.h" # include "sysprof-disk-usage.h" +# include "sysprof-energy-usage.h" # include "sysprof-instrument.h" # include "sysprof-memory-usage.h" # include "sysprof-network-usage.h" diff --git a/src/libsysprof-profile/tests/test-profiler.c b/src/libsysprof-profile/tests/test-profiler.c index 365a09d4..1dbc9d8c 100644 --- a/src/libsysprof-profile/tests/test-profiler.c +++ b/src/libsysprof-profile/tests/test-profiler.c @@ -119,6 +119,7 @@ main (int argc, sysprof_profiler_add_instrument (profiler, sysprof_battery_charge_new ()); sysprof_profiler_add_instrument (profiler, sysprof_cpu_usage_new ()); sysprof_profiler_add_instrument (profiler, sysprof_disk_usage_new ()); + sysprof_profiler_add_instrument (profiler, sysprof_energy_usage_new ()); sysprof_profiler_add_instrument (profiler, sysprof_memory_usage_new ()); sysprof_profiler_add_instrument (profiler, sysprof_network_usage_new ()); sysprof_profiler_add_instrument (profiler, sysprof_sampler_new ()); From 006618c5ae9ea6218634d28b23c1422f1c58652c Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 6 Jun 2023 16:15:28 -0700 Subject: [PATCH 0358/1030] libsysprof-profile: fix -- parsing with options --- src/libsysprof-profile/tests/test-profiler.c | 21 +++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/libsysprof-profile/tests/test-profiler.c b/src/libsysprof-profile/tests/test-profiler.c index 1dbc9d8c..54d28f82 100644 --- a/src/libsysprof-profile/tests/test-profiler.c +++ b/src/libsysprof-profile/tests/test-profiler.c @@ -89,21 +89,36 @@ sigint_handler (gpointer user_data) } int -main (int argc, +main (int argc, char *argv[]) { - g_autoptr(GOptionContext) context = g_option_context_new ("- Tests the SysprofProfiler"); + g_autoptr(GOptionContext) context = g_option_context_new ("[-- [COMMAND...]]"); g_autoptr(SysprofProfiler) profiler = NULL; g_autoptr(GError) error = NULL; + g_auto(GStrv) argv_copy = NULL; SysprofCaptureWriter *writer = NULL; SysprofCaptureReader *reader = NULL; g_autofd int trace_fd = -1; + int argv_copy_len = 0; main_loop = g_main_loop_new (NULL, FALSE); + argv_copy = g_new0 (char *, argc+1); + for (guint i = 0; i < argc; i++) + { + if (strcmp ("--", argv[i]) == 0) + { + argv_copy[i] = NULL; + break; + } + + argv_copy[i] = g_strdup (argv[i]); + argv_copy_len++; + } + g_option_context_add_main_entries (context, entries, NULL); - if (!g_option_context_parse (context, &argc, &argv, &error)) + if (!g_option_context_parse (context, &argv_copy_len, &argv_copy, &error)) { g_printerr ("%s\n", error->message); return 1; From b0a3736ff4613c34160b6f83f8b91aa56a0c1c99 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 6 Jun 2023 16:15:45 -0700 Subject: [PATCH 0359/1030] libsysprof-profile: add memprof to profiler test --- src/libsysprof-profile/tests/test-profiler.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/libsysprof-profile/tests/test-profiler.c b/src/libsysprof-profile/tests/test-profiler.c index 54d28f82..fd056db8 100644 --- a/src/libsysprof-profile/tests/test-profiler.c +++ b/src/libsysprof-profile/tests/test-profiler.c @@ -18,6 +18,8 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ +#include "config.h" + #include #include @@ -26,8 +28,10 @@ static GMainLoop *main_loop; static char *capture_file; static SysprofRecording *active_recording; +static gboolean memprof; static const GOptionEntry entries[] = { { "capture", 'c', 0, G_OPTION_ARG_FILENAME, &capture_file, "The file to capture into", "CAPTURE" }, + { "memprof", 'm', 0, G_OPTION_ARG_NONE, &memprof, "Do memory allocation tracking on subprocess" }, { 0 } }; @@ -148,6 +152,10 @@ main (int argc, sysprof_spawnable_append_args (spawnable, (const char * const *)&argv[i+1]); sysprof_profiler_set_spawnable (profiler, spawnable); + if (memprof) + sysprof_spawnable_add_ld_preload (spawnable, + PACKAGE_LIBDIR "/libsysprof-memory-" API_VERSION_S ".so"); + trace_fd = sysprof_spawnable_add_trace_fd (spawnable, NULL); } } From 1e675e92e47a0fb6e59e8f5dfc1f73bec6fda864 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 6 Jun 2023 16:30:38 -0700 Subject: [PATCH 0360/1030] libsysprof-profile: add malloc tracing instrument --- src/libsysprof-profile/meson.build | 2 + .../sysprof-malloc-tracing.c | 73 +++++++++++++++++++ .../sysprof-malloc-tracing.h | 42 +++++++++++ src/libsysprof-profile/sysprof-profile.h | 1 + src/libsysprof-profile/tests/test-profiler.c | 9 ++- 5 files changed, 123 insertions(+), 4 deletions(-) create mode 100644 src/libsysprof-profile/sysprof-malloc-tracing.c create mode 100644 src/libsysprof-profile/sysprof-malloc-tracing.h diff --git a/src/libsysprof-profile/meson.build b/src/libsysprof-profile/meson.build index 7277ab98..36a80eb0 100644 --- a/src/libsysprof-profile/meson.build +++ b/src/libsysprof-profile/meson.build @@ -5,6 +5,7 @@ libsysprof_profile_public_sources = [ 'sysprof-energy-usage.c', 'sysprof-instrument.c', 'sysprof-memory-usage.c', + 'sysprof-malloc-tracing.c', 'sysprof-network-usage.c', 'sysprof-profiler.c', 'sysprof-proxied-instrument.c', @@ -30,6 +31,7 @@ libsysprof_profile_public_headers = [ 'sysprof-energy-usage.h', 'sysprof-instrument.h', 'sysprof-memory-usage.h', + 'sysprof-malloc-tracing.h', 'sysprof-network-usage.h', 'sysprof-profiler.h', 'sysprof-proxied-instrument.h', diff --git a/src/libsysprof-profile/sysprof-malloc-tracing.c b/src/libsysprof-profile/sysprof-malloc-tracing.c new file mode 100644 index 00000000..e9bcae5c --- /dev/null +++ b/src/libsysprof-profile/sysprof-malloc-tracing.c @@ -0,0 +1,73 @@ +/* sysprof-malloc-tracing.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-malloc-tracing.h" +#include "sysprof-instrument-private.h" +#include "sysprof-recording-private.h" +#include "sysprof-spawnable.h" + +struct _SysprofMallocTracing +{ + SysprofInstrument parent_instance; +}; + +struct _SysprofMallocTracingClass +{ + SysprofInstrumentClass parent_class; +}; + +G_DEFINE_FINAL_TYPE (SysprofMallocTracing, sysprof_malloc_tracing, SYSPROF_TYPE_INSTRUMENT) + +static DexFuture * +sysprof_malloc_tracing_prepare (SysprofInstrument *instrument, + SysprofRecording *recording) +{ + SysprofSpawnable *spawnable; + + g_assert (SYSPROF_IS_MALLOC_TRACING (instrument)); + g_assert (SYSPROF_IS_RECORDING (recording)); + + if ((spawnable = _sysprof_recording_get_spawnable (recording))) + sysprof_spawnable_add_ld_preload (spawnable, + PACKAGE_LIBDIR"/libsysprof-memory-" API_VERSION_S ".so"); + + return dex_future_new_for_boolean (TRUE); +} + +static void +sysprof_malloc_tracing_class_init (SysprofMallocTracingClass *klass) +{ + SysprofInstrumentClass *instrument_class = SYSPROF_INSTRUMENT_CLASS (klass); + + instrument_class->prepare = sysprof_malloc_tracing_prepare; +} + +static void +sysprof_malloc_tracing_init (SysprofMallocTracing *self) +{ +} + +SysprofInstrument * +sysprof_malloc_tracing_new (void) +{ + return g_object_new (SYSPROF_TYPE_MALLOC_TRACING, NULL); +} diff --git a/src/libsysprof-profile/sysprof-malloc-tracing.h b/src/libsysprof-profile/sysprof-malloc-tracing.h new file mode 100644 index 00000000..2f30e64b --- /dev/null +++ b/src/libsysprof-profile/sysprof-malloc-tracing.h @@ -0,0 +1,42 @@ +/* sysprof-malloc-tracing.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-instrument.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_MALLOC_TRACING (sysprof_malloc_tracing_get_type()) +#define SYSPROF_IS_MALLOC_TRACING(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_MALLOC_TRACING) +#define SYSPROF_MALLOC_TRACING(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_MALLOC_TRACING, SysprofMallocTracing) +#define SYSPROF_MALLOC_TRACING_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_MALLOC_TRACING, SysprofMallocTracingClass) + +typedef struct _SysprofMallocTracing SysprofMallocTracing; +typedef struct _SysprofMallocTracingClass SysprofMallocTracingClass; + +SYSPROF_AVAILABLE_IN_ALL +GType sysprof_malloc_tracing_get_type (void) G_GNUC_CONST; +SYSPROF_AVAILABLE_IN_ALL +SysprofInstrument *sysprof_malloc_tracing_new (void); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofMallocTracing, g_object_unref) + +G_END_DECLS diff --git a/src/libsysprof-profile/sysprof-profile.h b/src/libsysprof-profile/sysprof-profile.h index 067af039..a33d1ec9 100644 --- a/src/libsysprof-profile/sysprof-profile.h +++ b/src/libsysprof-profile/sysprof-profile.h @@ -30,6 +30,7 @@ G_BEGIN_DECLS # include "sysprof-disk-usage.h" # include "sysprof-energy-usage.h" # include "sysprof-instrument.h" +# include "sysprof-malloc-tracing.h" # include "sysprof-memory-usage.h" # include "sysprof-network-usage.h" # include "sysprof-profiler.h" diff --git a/src/libsysprof-profile/tests/test-profiler.c b/src/libsysprof-profile/tests/test-profiler.c index fd056db8..a4554552 100644 --- a/src/libsysprof-profile/tests/test-profiler.c +++ b/src/libsysprof-profile/tests/test-profiler.c @@ -143,6 +143,9 @@ main (int argc, sysprof_profiler_add_instrument (profiler, sysprof_network_usage_new ()); sysprof_profiler_add_instrument (profiler, sysprof_sampler_new ()); + if (memprof) + sysprof_profiler_add_instrument (profiler, sysprof_malloc_tracing_new ()); + for (int i = 1; i < argc; i++) { if (strcmp (argv[i], "--") == 0 && i+1 < argc) @@ -152,11 +155,9 @@ main (int argc, sysprof_spawnable_append_args (spawnable, (const char * const *)&argv[i+1]); sysprof_profiler_set_spawnable (profiler, spawnable); - if (memprof) - sysprof_spawnable_add_ld_preload (spawnable, - PACKAGE_LIBDIR "/libsysprof-memory-" API_VERSION_S ".so"); - trace_fd = sysprof_spawnable_add_trace_fd (spawnable, NULL); + + break; } } From 8202a40f4d95c7d81286ee9690300d80cae886c7 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 6 Jun 2023 16:32:21 -0700 Subject: [PATCH 0361/1030] preload: hoist preloads into src/ directory They will get used from libsysprof-profile, but it's nice to have them a directory up going forward. --- src/libsysprof/meson.build | 2 -- src/meson.build | 1 + src/{libsysprof => }/preload/backtrace-helper.h | 0 src/{libsysprof => }/preload/gconstructor.h | 0 src/{libsysprof => }/preload/meson.build | 0 src/{libsysprof => }/preload/sysprof-memory-collector.c | 0 src/{libsysprof => }/preload/sysprof-speedtrack-collector.c | 0 src/{libsysprof => }/preload/sysprof-tracer.c | 0 8 files changed, 1 insertion(+), 2 deletions(-) rename src/{libsysprof => }/preload/backtrace-helper.h (100%) rename src/{libsysprof => }/preload/gconstructor.h (100%) rename src/{libsysprof => }/preload/meson.build (100%) rename src/{libsysprof => }/preload/sysprof-memory-collector.c (100%) rename src/{libsysprof => }/preload/sysprof-speedtrack-collector.c (100%) rename src/{libsysprof => }/preload/sysprof-tracer.c (100%) diff --git a/src/libsysprof/meson.build b/src/libsysprof/meson.build index fa0f5263..2e3af830 100644 --- a/src/libsysprof/meson.build +++ b/src/libsysprof/meson.build @@ -196,5 +196,3 @@ pkgconfig.generate( install_headers(libsysprof_public_headers, subdir: sysprof_header_subdir) endif - -subdir('preload') diff --git a/src/meson.build b/src/meson.build index c735b46c..616fcbf8 100644 --- a/src/meson.build +++ b/src/meson.build @@ -64,6 +64,7 @@ endif if need_libsysprof_profile subdir('libsysprof-profile') + subdir('preload') endif if get_option('sysprofd') == 'bundled' diff --git a/src/libsysprof/preload/backtrace-helper.h b/src/preload/backtrace-helper.h similarity index 100% rename from src/libsysprof/preload/backtrace-helper.h rename to src/preload/backtrace-helper.h diff --git a/src/libsysprof/preload/gconstructor.h b/src/preload/gconstructor.h similarity index 100% rename from src/libsysprof/preload/gconstructor.h rename to src/preload/gconstructor.h diff --git a/src/libsysprof/preload/meson.build b/src/preload/meson.build similarity index 100% rename from src/libsysprof/preload/meson.build rename to src/preload/meson.build diff --git a/src/libsysprof/preload/sysprof-memory-collector.c b/src/preload/sysprof-memory-collector.c similarity index 100% rename from src/libsysprof/preload/sysprof-memory-collector.c rename to src/preload/sysprof-memory-collector.c diff --git a/src/libsysprof/preload/sysprof-speedtrack-collector.c b/src/preload/sysprof-speedtrack-collector.c similarity index 100% rename from src/libsysprof/preload/sysprof-speedtrack-collector.c rename to src/preload/sysprof-speedtrack-collector.c diff --git a/src/libsysprof/preload/sysprof-tracer.c b/src/preload/sysprof-tracer.c similarity index 100% rename from src/libsysprof/preload/sysprof-tracer.c rename to src/preload/sysprof-tracer.c From 44e9da269268818b188f95b0608ef3872e9ac288 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 6 Jun 2023 16:44:50 -0700 Subject: [PATCH 0362/1030] callgraph: be more graceful with unwind failures --- src/libsysprof-analyze/sysprof-callgraph.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-callgraph.c b/src/libsysprof-analyze/sysprof-callgraph.c index f77c7e9f..aff9b6b3 100644 --- a/src/libsysprof-analyze/sysprof-callgraph.c +++ b/src/libsysprof-analyze/sysprof-callgraph.c @@ -153,7 +153,7 @@ sysprof_callgraph_add_trace (SysprofCallgraph *self, SysprofCallgraphNode *parent; g_assert (SYSPROF_IS_CALLGRAPH (self)); - g_assert (n_symbols > 2); + g_assert (n_symbols >= 2); g_assert (symbols[n_symbols-1] == self->everything); parent = &self->root; @@ -232,7 +232,6 @@ sysprof_callgraph_add_traceable (SysprofCallgraph *self, stack_depth, &final_context); - g_assert (n_symbols > 0); g_assert (n_symbols <= stack_depth); /* We saved 3 extra spaces for these above so that we can From 3d7a6ca43b780f4452cfb4817d3151c9aa0b9765 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 6 Jun 2023 16:45:02 -0700 Subject: [PATCH 0363/1030] libsysprof-profile: set default environment for spawnable --- src/libsysprof-profile/sysprof-spawnable.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libsysprof-profile/sysprof-spawnable.c b/src/libsysprof-profile/sysprof-spawnable.c index 733b0080..4c9e0a56 100644 --- a/src/libsysprof-profile/sysprof-spawnable.c +++ b/src/libsysprof-profile/sysprof-spawnable.c @@ -123,6 +123,8 @@ sysprof_spawnable_init (SysprofSpawnable *self) { self->next_fd = 3; + self->environ = g_get_environ (); + self->argv = g_ptr_array_new_with_free_func (g_free); g_ptr_array_add (self->argv, NULL); From 663ae8746a3f39caaf2ce8bf749158ff8bd5e8e7 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 6 Jun 2023 16:45:27 -0700 Subject: [PATCH 0364/1030] libsysprof-analyze: add list model index for allocation records --- src/libsysprof-analyze/sysprof-document.c | 21 +++++++++++++++++++++ src/libsysprof-analyze/sysprof-document.h | 2 ++ 2 files changed, 23 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index 4d3b6d2a..66ed1823 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -62,6 +62,7 @@ struct _SysprofDocument SysprofStrings *strings; + EggBitset *allocations; EggBitset *file_chunks; EggBitset *samples; EggBitset *traceables; @@ -210,6 +211,7 @@ sysprof_document_finalize (GObject *object) g_clear_pointer (&self->mapped_file, g_mapped_file_unref); g_clear_pointer (&self->frames, g_array_unref); + g_clear_pointer (&self->allocations, egg_bitset_unref); g_clear_pointer (&self->ctrdefs, egg_bitset_unref); g_clear_pointer (&self->ctrsets, egg_bitset_unref); g_clear_pointer (&self->file_chunks, egg_bitset_unref); @@ -250,6 +252,7 @@ sysprof_document_init (SysprofDocument *self) self->counter_id_to_values = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_array_unref); + self->allocations = egg_bitset_new_empty (); self->ctrdefs = egg_bitset_new_empty (); self->ctrsets = egg_bitset_new_empty (); self->file_chunks = egg_bitset_new_empty (); @@ -668,6 +671,7 @@ sysprof_document_load_worker (GTask *task, switch ((int)tainted->type) { case SYSPROF_CAPTURE_FRAME_ALLOCATION: + egg_bitset_add (self->allocations, self->frames->len); egg_bitset_add (self->traceables, self->frames->len); break; @@ -1018,6 +1022,23 @@ sysprof_document_list_traceables (SysprofDocument *self) return _sysprof_document_bitset_index_new (G_LIST_MODEL (self), self->traceables); } +/** + * sysprof_document_list_allocations: + * @self: a #SysprofDocument + * + * Gets a #GListModel containing #SysprofDocumentAllocation found within + * the #SysprofDocument. + * + * Returns: (transfer full): a #GListModel of #SysprofDocumentAllocation + */ +GListModel * +sysprof_document_list_allocations (SysprofDocument *self) +{ + g_return_val_if_fail (SYSPROF_IS_DOCUMENT (self), NULL); + + return _sysprof_document_bitset_index_new (G_LIST_MODEL (self), self->allocations); +} + /** * sysprof_document_list_samples: * @self: a #SysprofDocument diff --git a/src/libsysprof-analyze/sysprof-document.h b/src/libsysprof-analyze/sysprof-document.h index 4cb29292..cd0ac1a4 100644 --- a/src/libsysprof-analyze/sysprof-document.h +++ b/src/libsysprof-analyze/sysprof-document.h @@ -44,6 +44,8 @@ GListModel *sysprof_document_list_files (SysprofDocument SYSPROF_AVAILABLE_IN_ALL GListModel *sysprof_document_list_traceables (SysprofDocument *self); SYSPROF_AVAILABLE_IN_ALL +GListModel *sysprof_document_list_allocations (SysprofDocument *self); +SYSPROF_AVAILABLE_IN_ALL GListModel *sysprof_document_list_samples (SysprofDocument *self); SYSPROF_AVAILABLE_IN_ALL GListModel *sysprof_document_list_processes (SysprofDocument *self); From f3ab23ed18e52ef24474b0c81344943d8434eeea Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 6 Jun 2023 16:45:51 -0700 Subject: [PATCH 0365/1030] tools: allow showing basic callgraph for memory The columns aren't all that useful, but it is to show that augmentation will work for these things too. --- src/tools/callgraph.c | 53 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 45 insertions(+), 8 deletions(-) diff --git a/src/tools/callgraph.c b/src/tools/callgraph.c index da67c019..66c439ee 100644 --- a/src/tools/callgraph.c +++ b/src/tools/callgraph.c @@ -27,8 +27,10 @@ static GMainLoop *main_loop; static char *kallsyms_path; static char *filename; static double total; +static gboolean memprof; static const GOptionEntry entries[] = { { "kallsyms", 'k', 0, G_OPTION_ARG_FILENAME, &kallsyms_path, "The path to kallsyms to use for decoding", "PATH" }, + { "memprof", 'm', 0, G_OPTION_ARG_NONE, &memprof, "Read memory callgraph instead of samples" }, { 0 } }; @@ -345,10 +347,10 @@ callgraph_cb (GObject *object, } static void -augment_cb (SysprofCallgraph *callgraph, - SysprofCallgraphNode *node, - SysprofDocumentFrame *frame, - gpointer user_data) +augment_sample_cb (SysprofCallgraph *callgraph, + SysprofCallgraphNode *node, + SysprofDocumentFrame *frame, + gpointer user_data) { Augment *aug; @@ -367,6 +369,36 @@ augment_cb (SysprofCallgraph *callgraph, } } +static void +augment_memprof_cb (SysprofCallgraph *callgraph, + SysprofCallgraphNode *node, + SysprofDocumentFrame *frame, + gpointer user_data) +{ + SysprofDocumentAllocation *alloc = (SysprofDocumentAllocation *)frame; + Augment *aug; + gint64 size; + + g_assert (SYSPROF_IS_CALLGRAPH (callgraph)); + g_assert (node != NULL); + g_assert (SYSPROF_IS_DOCUMENT_ALLOCATION (alloc)); + g_assert (user_data == NULL); + + size = sysprof_document_allocation_get_size (alloc); + + if (sysprof_document_allocation_is_free (alloc)) + return; + + aug = sysprof_callgraph_get_augment (callgraph, node); + aug->size += size; + + for (; node; node = sysprof_callgraph_node_parent (node)) + { + aug = sysprof_callgraph_get_augment (callgraph, node); + aug->total += size; + } +} + int main (int argc, char *argv[]) @@ -375,7 +407,7 @@ main (int argc, g_autoptr(SysprofDocumentLoader) loader = NULL; g_autoptr(SysprofDocument) document = NULL; g_autoptr(SysprofMultiSymbolizer) multi = NULL; - g_autoptr(GListModel) samples = NULL; + g_autoptr(GListModel) model = NULL; g_autoptr(GError) error = NULL; gtk_init (); @@ -423,11 +455,16 @@ main (int argc, g_error ("Failed to load document: %s", error->message); g_print ("Loaded and symbolized. Generating callgraph...\n"); - samples = sysprof_document_list_samples (document); + if (memprof) + model = sysprof_document_list_allocations (document); + else + model = sysprof_document_list_samples (document); sysprof_document_callgraph_async (document, - samples, + model, sizeof (Augment), - augment_cb, NULL, NULL, + memprof ? augment_memprof_cb : augment_sample_cb, + NULL, + NULL, NULL, callgraph_cb, main_loop); From 434cc6ba22afa662913c0803e7f2d8d0071657f3 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 6 Jun 2023 16:48:50 -0700 Subject: [PATCH 0366/1030] tools: give some feedback about callgraph type --- src/tools/callgraph.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tools/callgraph.c b/src/tools/callgraph.c index 66c439ee..011a6b14 100644 --- a/src/tools/callgraph.c +++ b/src/tools/callgraph.c @@ -454,7 +454,8 @@ main (int argc, if (!(document = sysprof_document_loader_load (loader, NULL, &error))) g_error ("Failed to load document: %s", error->message); - g_print ("Loaded and symbolized. Generating callgraph...\n"); + g_print ("Loaded and symbolized. Generating callgraph of %s ...\n", + memprof ? "allocations" : "samples"); if (memprof) model = sysprof_document_list_allocations (document); else From 275869b198c637b7b907656867638b7fcc81afd9 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 6 Jun 2023 17:30:13 -0700 Subject: [PATCH 0367/1030] libsysprof-analyze: implement basic callers listing This doesn't give us augmentation data like we would have from the existing callgraphs, but we can deal with that later. --- src/libsysprof-analyze/sysprof-callgraph.c | 67 +++++++++++++++++++++- src/libsysprof-analyze/sysprof-callgraph.h | 12 ++-- 2 files changed, 74 insertions(+), 5 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-callgraph.c b/src/libsysprof-analyze/sysprof-callgraph.c index aff9b6b3..bf3af842 100644 --- a/src/libsysprof-analyze/sysprof-callgraph.c +++ b/src/libsysprof-analyze/sysprof-callgraph.c @@ -37,6 +37,8 @@ struct _SysprofCallgraph SysprofSymbol *everything; + GHashTable *callers; + gsize augment_size; SysprofAugmentationFunc augment_func; gpointer augment_func_data; @@ -120,6 +122,8 @@ sysprof_callgraph_finalize (GObject *object) { SysprofCallgraph *self = (SysprofCallgraph *)object; + g_clear_pointer (&self->callers, g_hash_table_unref); + g_clear_object (&self->document); g_clear_object (&self->traceables); g_clear_object (&self->everything); @@ -142,15 +146,45 @@ static void sysprof_callgraph_init (SysprofCallgraph *self) { self->everything = _sysprof_symbol_new (g_ref_string_new_intern ("[Everything]"), NULL, NULL, 0, 0); + self->callers = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_ptr_array_unref); self->root.symbol = self->everything; } +static void +sysprof_callgraph_populate_callers (SysprofCallgraph *self, + SysprofCallgraphNode *node) +{ + GHashTable *hash; + + g_assert (SYSPROF_IS_CALLGRAPH (self)); + g_assert (node != NULL); + + hash = self->callers; + + for (const SysprofCallgraphNode *iter = node; + iter != NULL && iter->parent != NULL; + iter = iter->parent) + { + GPtrArray *callers; + + if (!(callers = g_hash_table_lookup (hash, iter->symbol))) + { + callers = g_ptr_array_new (); + g_hash_table_insert (hash, iter->symbol, callers); + } + + g_assert (iter->parent->symbol != NULL); + + g_ptr_array_add (callers, iter->parent->symbol); + } +} + static SysprofCallgraphNode * sysprof_callgraph_add_trace (SysprofCallgraph *self, SysprofSymbol **symbols, guint n_symbols) { - SysprofCallgraphNode *parent; + SysprofCallgraphNode *parent = NULL; g_assert (SYSPROF_IS_CALLGRAPH (self)); g_assert (n_symbols >= 2); @@ -202,6 +236,8 @@ sysprof_callgraph_add_trace (SysprofCallgraph *self, parent = node; } + sysprof_callgraph_populate_callers (self, parent); + return parent; } @@ -341,3 +377,32 @@ sysprof_callgraph_node_parent (SysprofCallgraphNode *node) { return node->parent; } + +/** + * sysprof_callgraph_list_callers: + * @self: a #SysprofCallgraph + * @node: a #SysprofCallgraphFrame + * + * Gets a list of #SysprofSymbol that call @node. + * + * Returns: (trasfer full): a #GListModel of #SysprofSymbol + */ +GListModel * +sysprof_callgraph_list_callers (SysprofCallgraph *self, + SysprofCallgraphFrame *frame) +{ + SysprofSymbol *symbol; + GListStore *store; + GPtrArray *callers; + + g_return_val_if_fail (SYSPROF_IS_CALLGRAPH (self), NULL); + g_return_val_if_fail (SYSPROF_IS_CALLGRAPH_FRAME (frame), NULL); + + store = g_list_store_new (SYSPROF_TYPE_SYMBOL); + symbol = sysprof_callgraph_frame_get_symbol (frame); + + if ((callers = g_hash_table_lookup (self->callers, symbol))) + g_list_store_splice (store, 0, 0, callers->pdata, callers->len); + + return G_LIST_MODEL (store); +} diff --git a/src/libsysprof-analyze/sysprof-callgraph.h b/src/libsysprof-analyze/sysprof-callgraph.h index 5407b62d..1ae27b46 100644 --- a/src/libsysprof-analyze/sysprof-callgraph.h +++ b/src/libsysprof-analyze/sysprof-callgraph.h @@ -20,10 +20,11 @@ #pragma once -#include +#include #include +#include "sysprof-callgraph-frame.h" #include "sysprof-document-frame.h" G_BEGIN_DECLS @@ -56,9 +57,12 @@ typedef void (*SysprofAugmentationFunc) (SysprofCallgraph *callgraph, gpointer user_data); SYSPROF_AVAILABLE_IN_ALL -gpointer sysprof_callgraph_get_augment (SysprofCallgraph *callgraph, - SysprofCallgraphNode *node); +GListModel *sysprof_callgraph_list_callers (SysprofCallgraph *self, + SysprofCallgraphFrame *frame); SYSPROF_AVAILABLE_IN_ALL -SysprofCallgraphNode *sysprof_callgraph_node_parent (SysprofCallgraphNode *node); +gpointer sysprof_callgraph_get_augment (SysprofCallgraph *self, + SysprofCallgraphNode *node); +SYSPROF_AVAILABLE_IN_ALL +SysprofCallgraphNode *sysprof_callgraph_node_parent (SysprofCallgraphNode *node); G_END_DECLS From 6fc1049e6379c72166b6e63d4ba1e6d6de4777b1 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 6 Jun 2023 17:33:42 -0700 Subject: [PATCH 0368/1030] libsysprof-analyze: don't duplicate callers --- src/libsysprof-analyze/sysprof-callgraph.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libsysprof-analyze/sysprof-callgraph.c b/src/libsysprof-analyze/sysprof-callgraph.c index bf3af842..56d5c8a0 100644 --- a/src/libsysprof-analyze/sysprof-callgraph.c +++ b/src/libsysprof-analyze/sysprof-callgraph.c @@ -166,6 +166,7 @@ sysprof_callgraph_populate_callers (SysprofCallgraph *self, iter = iter->parent) { GPtrArray *callers; + guint pos; if (!(callers = g_hash_table_lookup (hash, iter->symbol))) { @@ -175,7 +176,8 @@ sysprof_callgraph_populate_callers (SysprofCallgraph *self, g_assert (iter->parent->symbol != NULL); - g_ptr_array_add (callers, iter->parent->symbol); + if (!g_ptr_array_find (callers, iter->parent->symbol, &pos)) + g_ptr_array_add (callers, iter->parent->symbol); } } From 2c46d6a4c17f9f22c85507a122f37c2a05b3ace3 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 8 Jun 2023 14:22:18 -0700 Subject: [PATCH 0369/1030] libsysprof-analyze: add listmodel of stacktraces containing symbol --- src/libsysprof-analyze/sysprof-callgraph.c | 56 +++++++++++++++++++--- src/libsysprof-analyze/sysprof-callgraph.h | 13 +++-- 2 files changed, 58 insertions(+), 11 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-callgraph.c b/src/libsysprof-analyze/sysprof-callgraph.c index 56d5c8a0..927b7ee3 100644 --- a/src/libsysprof-analyze/sysprof-callgraph.c +++ b/src/libsysprof-analyze/sysprof-callgraph.c @@ -22,10 +22,13 @@ #include "sysprof-callgraph-private.h" #include "sysprof-callgraph-frame-private.h" +#include "sysprof-document-bitset-index-private.h" #include "sysprof-document-private.h" #include "sysprof-document-traceable.h" #include "sysprof-symbol-private.h" +#include "eggbitset.h" + #define MAX_STACK_DEPTH 1024 struct _SysprofCallgraph @@ -37,6 +40,8 @@ struct _SysprofCallgraph SysprofSymbol *everything; + GHashTable *symbol_to_bitset; + GHashTable *callers; gsize augment_size; @@ -123,6 +128,7 @@ sysprof_callgraph_finalize (GObject *object) SysprofCallgraph *self = (SysprofCallgraph *)object; g_clear_pointer (&self->callers, g_hash_table_unref); + g_clear_pointer (&self->symbol_to_bitset, g_hash_table_unref); g_clear_object (&self->document); g_clear_object (&self->traceables); @@ -147,12 +153,14 @@ sysprof_callgraph_init (SysprofCallgraph *self) { self->everything = _sysprof_symbol_new (g_ref_string_new_intern ("[Everything]"), NULL, NULL, 0, 0); self->callers = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_ptr_array_unref); + self->symbol_to_bitset = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)egg_bitset_unref); self->root.symbol = self->everything; } static void sysprof_callgraph_populate_callers (SysprofCallgraph *self, - SysprofCallgraphNode *node) + SysprofCallgraphNode *node, + guint list_model_index) { GHashTable *hash; @@ -166,6 +174,7 @@ sysprof_callgraph_populate_callers (SysprofCallgraph *self, iter = iter->parent) { GPtrArray *callers; + EggBitset *bitset; guint pos; if (!(callers = g_hash_table_lookup (hash, iter->symbol))) @@ -174,6 +183,14 @@ sysprof_callgraph_populate_callers (SysprofCallgraph *self, g_hash_table_insert (hash, iter->symbol, callers); } + if (!(bitset = g_hash_table_lookup (self->symbol_to_bitset, iter->symbol))) + { + bitset = egg_bitset_new_empty (); + g_hash_table_insert (self->symbol_to_bitset, iter->symbol, bitset); + } + + egg_bitset_add (bitset, list_model_index); + g_assert (iter->parent->symbol != NULL); if (!g_ptr_array_find (callers, iter->parent->symbol, &pos)) @@ -184,7 +201,8 @@ sysprof_callgraph_populate_callers (SysprofCallgraph *self, static SysprofCallgraphNode * sysprof_callgraph_add_trace (SysprofCallgraph *self, SysprofSymbol **symbols, - guint n_symbols) + guint n_symbols, + guint list_model_index) { SysprofCallgraphNode *parent = NULL; @@ -238,14 +256,15 @@ sysprof_callgraph_add_trace (SysprofCallgraph *self, parent = node; } - sysprof_callgraph_populate_callers (self, parent); + sysprof_callgraph_populate_callers (self, parent, list_model_index); return parent; } static void sysprof_callgraph_add_traceable (SysprofCallgraph *self, - SysprofDocumentTraceable *traceable) + SysprofDocumentTraceable *traceable, + guint list_model_index) { SysprofAddressContext final_context; SysprofCallgraphNode *node; @@ -283,7 +302,7 @@ sysprof_callgraph_add_traceable (SysprofCallgraph *self, symbols[n_symbols++] = _sysprof_document_process_symbol (self->document, pid); symbols[n_symbols++] = self->everything; - node = sysprof_callgraph_add_trace (self, symbols, n_symbols); + node = sysprof_callgraph_add_trace (self, symbols, n_symbols, list_model_index); if (node && self->augment_func) self->augment_func (self, @@ -312,7 +331,7 @@ sysprof_callgraph_new_worker (GTask *task, { g_autoptr(SysprofDocumentTraceable) traceable = g_list_model_get_item (self->traceables, i); - sysprof_callgraph_add_traceable (self, traceable); + sysprof_callgraph_add_traceable (self, traceable, i); } g_task_return_pointer (task, g_object_ref (self), g_object_unref); @@ -408,3 +427,28 @@ sysprof_callgraph_list_callers (SysprofCallgraph *self, return G_LIST_MODEL (store); } + +/** + * sysprof_callgraph_list_traceables_for_symbol: + * @self: a #SysprofCallgraph + * @symbol: a #SysprofSymbol + * + * Gets a list of all the #SysprofTraceable within the callgraph + * that contain @symbol. + * + * Returns: (transfer full): a #GListModel of #SysprofTraceable + */ +GListModel * +sysprof_callgraph_list_traceables_for_symbol (SysprofCallgraph *self, + SysprofSymbol *symbol) +{ + EggBitset *bitset; + + g_return_val_if_fail (SYSPROF_IS_CALLGRAPH (self), NULL); + g_return_val_if_fail (SYSPROF_IS_SYMBOL (symbol), NULL); + + if ((bitset = g_hash_table_lookup (self->symbol_to_bitset, symbol))) + return _sysprof_document_bitset_index_new (self->traceables, bitset); + + return G_LIST_MODEL (g_list_store_new (SYSPROF_TYPE_DOCUMENT_TRACEABLE)); +} diff --git a/src/libsysprof-analyze/sysprof-callgraph.h b/src/libsysprof-analyze/sysprof-callgraph.h index 1ae27b46..97355731 100644 --- a/src/libsysprof-analyze/sysprof-callgraph.h +++ b/src/libsysprof-analyze/sysprof-callgraph.h @@ -57,12 +57,15 @@ typedef void (*SysprofAugmentationFunc) (SysprofCallgraph *callgraph, gpointer user_data); SYSPROF_AVAILABLE_IN_ALL -GListModel *sysprof_callgraph_list_callers (SysprofCallgraph *self, - SysprofCallgraphFrame *frame); +GListModel *sysprof_callgraph_list_callers (SysprofCallgraph *self, + SysprofCallgraphFrame *frame); SYSPROF_AVAILABLE_IN_ALL -gpointer sysprof_callgraph_get_augment (SysprofCallgraph *self, - SysprofCallgraphNode *node); +GListModel *sysprof_callgraph_list_traceables_for_symbol (SysprofCallgraph *self, + SysprofSymbol *symbol); SYSPROF_AVAILABLE_IN_ALL -SysprofCallgraphNode *sysprof_callgraph_node_parent (SysprofCallgraphNode *node); +gpointer sysprof_callgraph_get_augment (SysprofCallgraph *self, + SysprofCallgraphNode *node); +SYSPROF_AVAILABLE_IN_ALL +SysprofCallgraphNode *sysprof_callgraph_node_parent (SysprofCallgraphNode *node); G_END_DECLS From ca0cff64460270feffa18caa9051af828c4c37a7 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 8 Jun 2023 17:29:27 -0700 Subject: [PATCH 0370/1030] libsysprof-analyze: fix binary-nick usage --- src/libsysprof-analyze/sysprof-elf-symbolizer.c | 2 +- src/libsysprof-analyze/sysprof-kallsyms-symbolizer.c | 4 ++-- src/libsysprof-analyze/sysprof-symbol-private.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-elf-symbolizer.c b/src/libsysprof-analyze/sysprof-elf-symbolizer.c index afc779c8..dfd69e72 100644 --- a/src/libsysprof-analyze/sysprof-elf-symbolizer.c +++ b/src/libsysprof-analyze/sysprof-elf-symbolizer.c @@ -131,8 +131,8 @@ sysprof_elf_symbolizer_symbolize (SysprofSymbolizer *symbolizer, end_address++; return _sysprof_symbol_new (sysprof_strings_get (strings, name), - sysprof_strings_get (strings, sysprof_elf_get_nick (elf)), 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)); diff --git a/src/libsysprof-analyze/sysprof-kallsyms-symbolizer.c b/src/libsysprof-analyze/sysprof-kallsyms-symbolizer.c index b87c42aa..0995ea97 100644 --- a/src/libsysprof-analyze/sysprof-kallsyms-symbolizer.c +++ b/src/libsysprof-analyze/sysprof-kallsyms-symbolizer.c @@ -276,8 +276,8 @@ sysprof_kallsyms_symbolizer_symbolize (SysprofSymbolizer *symbolizer, if (address >= ksym->address && (address < next->address || next->address == 0)) return _sysprof_symbol_new (g_ref_string_acquire (ksym->name), - g_ref_string_acquire (linux_string), NULL, + g_ref_string_acquire (linux_string), ksym->address, next->address ? next->address : ksym->address + LAST_SYMBOL_LEN); @@ -295,8 +295,8 @@ failure: g_snprintf (name, sizeof name, "In Kernel+0x%"G_GINT64_MODIFIER"x", address); return _sysprof_symbol_new (sysprof_strings_get (strings, name), - g_ref_string_acquire (linux_string), NULL, + g_ref_string_acquire (linux_string), address, address + 1); } diff --git a/src/libsysprof-analyze/sysprof-symbol-private.h b/src/libsysprof-analyze/sysprof-symbol-private.h index 4eab3e37..9961c691 100644 --- a/src/libsysprof-analyze/sysprof-symbol-private.h +++ b/src/libsysprof-analyze/sysprof-symbol-private.h @@ -42,8 +42,8 @@ struct _SysprofSymbol }; SysprofSymbol *_sysprof_symbol_new (GRefString *name, - GRefString *binary_nick, GRefString *binary_path, + GRefString *binary_nick, SysprofAddress begin_address, SysprofAddress end_address); From 6570087c7c57bf732adf6ffcd6f71e14371cd569 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 8 Jun 2023 17:38:15 -0700 Subject: [PATCH 0371/1030] libsysprof-gtk: start on GTK widgetry library This is ultimately to replace libsysprof-ui, and will focus on the display of capture files. Recording will be left to the sysprof application in terms of UI, and other applications like IDEs will use the libsysprof-profile API directly along with libsysprof-gtk for visualizing. --- .../libsysprof-gtk.gresource.xml | 6 + src/libsysprof-gtk/meson.build | 74 ++++ .../sysprof-callgraph-view-private.h | 55 +++ src/libsysprof-gtk/sysprof-callgraph-view.c | 375 ++++++++++++++++++ src/libsysprof-gtk/sysprof-callgraph-view.h | 53 +++ src/libsysprof-gtk/sysprof-callgraph-view.ui | 90 +++++ src/libsysprof-gtk/sysprof-gtk.h | 30 ++ .../sysprof-weighted-callgraph-view.c | 85 ++++ .../sysprof-weighted-callgraph-view.h | 42 ++ src/libsysprof-gtk/tests/meson.build | 31 ++ src/libsysprof-gtk/tests/test-callgraph.c | 112 ++++++ src/meson.build | 1 + 12 files changed, 954 insertions(+) create mode 100644 src/libsysprof-gtk/libsysprof-gtk.gresource.xml create mode 100644 src/libsysprof-gtk/meson.build create mode 100644 src/libsysprof-gtk/sysprof-callgraph-view-private.h create mode 100644 src/libsysprof-gtk/sysprof-callgraph-view.c create mode 100644 src/libsysprof-gtk/sysprof-callgraph-view.h create mode 100644 src/libsysprof-gtk/sysprof-callgraph-view.ui create mode 100644 src/libsysprof-gtk/sysprof-gtk.h create mode 100644 src/libsysprof-gtk/sysprof-weighted-callgraph-view.c create mode 100644 src/libsysprof-gtk/sysprof-weighted-callgraph-view.h create mode 100644 src/libsysprof-gtk/tests/meson.build create mode 100644 src/libsysprof-gtk/tests/test-callgraph.c diff --git a/src/libsysprof-gtk/libsysprof-gtk.gresource.xml b/src/libsysprof-gtk/libsysprof-gtk.gresource.xml new file mode 100644 index 00000000..2a36c015 --- /dev/null +++ b/src/libsysprof-gtk/libsysprof-gtk.gresource.xml @@ -0,0 +1,6 @@ + + + + sysprof-callgraph-view.ui + + diff --git a/src/libsysprof-gtk/meson.build b/src/libsysprof-gtk/meson.build new file mode 100644 index 00000000..2c517f6e --- /dev/null +++ b/src/libsysprof-gtk/meson.build @@ -0,0 +1,74 @@ +libsysprof_gtk_public_sources = [ + 'sysprof-callgraph-view.c', + 'sysprof-weighted-callgraph-view.c', +] + +libsysprof_gtk_private_sources = [ +] + +libsysprof_gtk_public_headers = [ + 'sysprof-gtk.h', + + 'sysprof-callgraph-view.h', + 'sysprof-weighted-callgraph-view.h', +] + +libsysprof_gtk_deps = [ + dependency('gtk4', version: gtk_req_version), + + libsysprof_analyze_dep, +] + +libsysprof_gtk_resources = gnome.compile_resources( + 'libsysprof-gtk-resources', + 'libsysprof-gtk.gresource.xml', + c_name: 'libsysprof_gtk', +) + +libsysprof_gtk_static = static_library( + 'sysprof-gtk-@0@'.format(soname_major_version), + (libsysprof_gtk_public_sources + + libsysprof_gtk_private_sources + + libsysprof_gtk_resources + + mapped_ring_buffer_sources), + + include_directories: [include_directories('.'), + ipc_include_dirs, + libsysprof_capture_include_dirs], + dependencies: libsysprof_gtk_deps, + gnu_symbol_visibility: 'hidden', +) + +libsysprof_gtk_static_dep = declare_dependency( + link_with: libsysprof_gtk_static, + dependencies: libsysprof_gtk_deps, + include_directories: [include_directories('.'), + libsysprof_capture_include_dirs], +) + +libsysprof_gtk = library('sysprof-gtk-@0@'.format(soname_major_version), + dependencies: [libsysprof_gtk_static_dep], + gnu_symbol_visibility: 'hidden', + version: '@0@.0.0'.format(soname_major_version), + darwin_versions: '@0@.0'.format(soname_major_version), + install: true, +) + +libsysprof_gtk_dep = declare_dependency( + link_with: libsysprof_gtk, + dependencies: libsysprof_gtk_deps, + include_directories: [include_directories('.'), libsysprof_capture_include_dirs], +) +meson.override_dependency('sysprof-gtk-@0@'.format(soname_major_version), libsysprof_gtk_dep) + +pkgconfig.generate(libsysprof_gtk, + subdirs: [sysprof_header_subdir], + description: 'Provides GTK widgets for sysprof visualizion', + install_dir: join_paths(get_option('libdir'), 'pkgconfig'), + requires: ['gtk4'], + variables: ['datadir=' + datadir_for_pc_file], +) + +install_headers(libsysprof_gtk_public_headers, subdir: sysprof_header_subdir) + +subdir('tests') diff --git a/src/libsysprof-gtk/sysprof-callgraph-view-private.h b/src/libsysprof-gtk/sysprof-callgraph-view-private.h new file mode 100644 index 00000000..b33a482d --- /dev/null +++ b/src/libsysprof-gtk/sysprof-callgraph-view-private.h @@ -0,0 +1,55 @@ +/* sysprof-callgraph-view-private.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-callgraph-view.h" + +G_BEGIN_DECLS + +#define SYSPROF_CALLGRAPH_VIEW_GET_CLASS(instance) G_TYPE_INSTANCE_GET_CLASS(instance, SYSPROF_TYPE_CALLGRAPH_VIEW, SysprofCallgraphViewClass) + +struct _SysprofCallgraphView +{ + GtkWidget parent_instance; + + SysprofDocument *document; + GListModel *traceables; + + GtkColumnView *column_view; + GtkWidget *scrolled_window; + + GCancellable *cancellable; + + guint reload_source; +}; + +struct _SysprofCallgraphViewClass +{ + GtkWidgetClass parent_class; + + gsize augment_size; + void (*augment_func) (SysprofCallgraph *callgraph, + SysprofCallgraphNode *node, + SysprofDocumentFrame *frame, + gpointer user_data); +}; + +G_END_DECLS diff --git a/src/libsysprof-gtk/sysprof-callgraph-view.c b/src/libsysprof-gtk/sysprof-callgraph-view.c new file mode 100644 index 00000000..7d2a5b23 --- /dev/null +++ b/src/libsysprof-gtk/sysprof-callgraph-view.c @@ -0,0 +1,375 @@ +/* sysprof-callgraph-view.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "libsysprof-gtk-resources.h" + +#include "sysprof-callgraph-view-private.h" + +enum { + PROP_0, + PROP_DOCUMENT, + PROP_TRACEABLES, + N_PROPS +}; + +G_DEFINE_ABSTRACT_TYPE (SysprofCallgraphView, sysprof_callgraph_view, GTK_TYPE_WIDGET) + +static GParamSpec *properties [N_PROPS]; + +static gboolean +sysprof_callgraph_view_key_pressed_cb (GtkTreeExpander *expander, + guint keyval, + guint keycode, + GdkModifierType state, + GtkEventControllerKey *controller) +{ + GtkTreeListRow *row; + + g_assert (GTK_IS_TREE_EXPANDER (expander)); + g_assert (GTK_IS_EVENT_CONTROLLER_KEY (controller)); + + row = gtk_tree_expander_get_list_row (expander); + + if (keyval == GDK_KEY_space) + gtk_tree_list_row_set_expanded (row, !gtk_tree_list_row_get_expanded (row)); + else if (keyval == GDK_KEY_Right) + gtk_tree_list_row_set_expanded (row, TRUE); + else if (keyval == GDK_KEY_Left) + gtk_tree_list_row_set_expanded (row, FALSE); + else + return FALSE; + + return TRUE; +} + +static void +sysprof_callgraph_view_dispose (GObject *object) +{ + SysprofCallgraphView *self = (SysprofCallgraphView *)object; + + g_clear_handle_id (&self->reload_source, g_source_remove); + + g_clear_pointer ((GtkWidget **)&self->scrolled_window, gtk_widget_unparent); + + g_cancellable_cancel (self->cancellable); + g_clear_object (&self->cancellable); + + g_clear_object (&self->document); + g_clear_object (&self->traceables); + + G_OBJECT_CLASS (sysprof_callgraph_view_parent_class)->dispose (object); +} + +static void +sysprof_callgraph_view_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofCallgraphView *self = SYSPROF_CALLGRAPH_VIEW (object); + + switch (prop_id) + { + case PROP_DOCUMENT: + g_value_set_object (value, sysprof_callgraph_view_get_document (self)); + break; + + case PROP_TRACEABLES: + g_value_set_object (value, sysprof_callgraph_view_get_traceables (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_callgraph_view_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SysprofCallgraphView *self = SYSPROF_CALLGRAPH_VIEW (object); + + switch (prop_id) + { + case PROP_DOCUMENT: + sysprof_callgraph_view_set_document (self, g_value_get_object (value)); + break; + + case PROP_TRACEABLES: + sysprof_callgraph_view_set_traceables (self, g_value_get_object (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_callgraph_view_class_init (SysprofCallgraphViewClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->dispose = sysprof_callgraph_view_dispose; + object_class->get_property = sysprof_callgraph_view_get_property; + object_class->set_property = sysprof_callgraph_view_set_property; + + properties[PROP_DOCUMENT] = + g_param_spec_object ("document", NULL, NULL, + SYSPROF_TYPE_DOCUMENT, + (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + properties[PROP_TRACEABLES] = + g_param_spec_object ("traceables", NULL, NULL, + G_TYPE_LIST_MODEL, + (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); + + gtk_widget_class_set_template_from_resource (widget_class, "/libsysprof-gtk/sysprof-callgraph-view.ui"); + gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT); + gtk_widget_class_bind_template_child (widget_class, SysprofCallgraphView, column_view); + gtk_widget_class_bind_template_child (widget_class, SysprofCallgraphView, scrolled_window); + gtk_widget_class_bind_template_callback (widget_class, sysprof_callgraph_view_key_pressed_cb); + + klass->augment_size = GLIB_SIZEOF_VOID_P; + + g_resources_register (libsysprof_gtk_get_resource ()); +} + +static void +sysprof_callgraph_view_init (SysprofCallgraphView *self) +{ + gtk_widget_init_template (GTK_WIDGET (self)); +} + +static GListModel * +sysprof_callgraph_view_create_model_func (gpointer item, + gpointer user_data) +{ + if (g_list_model_get_n_items (G_LIST_MODEL (item)) > 0) + return g_object_ref (G_LIST_MODEL (item)); + + return NULL; +} + +static void +sysprof_callgraph_view_reload_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + SysprofDocument *document = (SysprofDocument *)object; + g_autoptr(SysprofCallgraphView) self = user_data; + g_autoptr(SysprofCallgraph) callgraph = NULL; + g_autoptr(GtkTreeListRowSorter) sorter = NULL; + g_autoptr(GtkMultiSelection) model = NULL; + g_autoptr(GtkSortListModel) sort_model = NULL; + g_autoptr(GtkTreeListModel) tree = NULL; + g_autoptr(GError) error = NULL; + GtkSorter *column_sorter; + + g_assert (SYSPROF_IS_DOCUMENT (document)); + g_assert (G_IS_ASYNC_RESULT (result)); + g_assert (SYSPROF_IS_CALLGRAPH_VIEW (self)); + + if (!(callgraph = sysprof_document_callgraph_finish (document, result, &error))) + { + g_debug ("Failed to generate callgraph: %s", error->message); + return; + } + + tree = gtk_tree_list_model_new (g_object_ref (G_LIST_MODEL (callgraph)), + FALSE, + FALSE, + sysprof_callgraph_view_create_model_func, + NULL, + NULL); + column_sorter = gtk_column_view_get_sorter (self->column_view); + sorter = gtk_tree_list_row_sorter_new (g_object_ref (column_sorter)); + sort_model = gtk_sort_list_model_new (g_object_ref (G_LIST_MODEL (tree)), + g_object_ref (GTK_SORTER (sorter))); + model = gtk_multi_selection_new (g_object_ref (G_LIST_MODEL (sort_model))); + gtk_column_view_set_model (self->column_view, GTK_SELECTION_MODEL (model)); +} + +static gboolean +sysprof_callgraph_view_reload (SysprofCallgraphView *self) +{ + g_assert (SYSPROF_IS_CALLGRAPH_VIEW (self)); + + g_clear_handle_id (&self->reload_source, g_source_remove); + + sysprof_document_callgraph_async (self->document, + self->traceables, + SYSPROF_CALLGRAPH_VIEW_GET_CLASS (self)->augment_size, + SYSPROF_CALLGRAPH_VIEW_GET_CLASS (self)->augment_func, + NULL, + NULL, + self->cancellable, + sysprof_callgraph_view_reload_cb, + g_object_ref (self)); + + return G_SOURCE_REMOVE; +} + +static void +sysprof_callgraph_view_queue_reload (SysprofCallgraphView *self) +{ + g_assert (SYSPROF_IS_CALLGRAPH_VIEW (self)); + + gtk_column_view_set_model (self->column_view, NULL); + + g_clear_handle_id (&self->reload_source, g_source_remove); + g_cancellable_cancel (self->cancellable); + + g_clear_object (&self->cancellable); + self->cancellable = g_cancellable_new (); + + if (self->document != NULL && self->traceables != NULL) + self->reload_source = g_idle_add_full (G_PRIORITY_LOW, + (GSourceFunc)sysprof_callgraph_view_reload, + g_object_ref (self), + g_object_unref); +} + +/** + * sysprof_callgraph_view_get_document: + * @self: a #SysprofCallgraphView + * + * Gets the document for the callgraph. + * + * Returns: (transfer none) (nullable): a #SysprofDocument or %NULL + */ +SysprofDocument * +sysprof_callgraph_view_get_document (SysprofCallgraphView *self) +{ + g_return_val_if_fail (SYSPROF_IS_CALLGRAPH_VIEW (self), NULL); + + return self->document; +} + +void +sysprof_callgraph_view_set_document (SysprofCallgraphView *self, + SysprofDocument *document) +{ + g_return_if_fail (SYSPROF_IS_CALLGRAPH_VIEW (self)); + + if (g_set_object (&self->document, document)) + { + sysprof_callgraph_view_queue_reload (self); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DOCUMENT]); + } +} + +/** + * sysprof_callgraph_view_get_traceables: + * @self: a #SysprofCallgraphView + * + * Gets the list of traceables to be displayed. + * + * Returns: (transfer none) (nullable): a #GListModel or %NULL + */ +GListModel * +sysprof_callgraph_view_get_traceables (SysprofCallgraphView *self) +{ + g_return_val_if_fail (SYSPROF_IS_CALLGRAPH_VIEW (self), NULL); + + return self->traceables; +} + +void +sysprof_callgraph_view_set_traceables (SysprofCallgraphView *self, + GListModel *traceables) +{ + g_return_if_fail (SYSPROF_IS_CALLGRAPH_VIEW (self)); + g_return_if_fail (!traceables || G_IS_LIST_MODEL (traceables)); + + if (g_set_object (&self->traceables, traceables)) + { + sysprof_callgraph_view_queue_reload (self); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_TRACEABLES]); + } +} + +#if 0 +/** + * sysprof_callgraph_view_get_callgraph: + * @self: a #SysprofCallgraphView + * + * Gets the #SysprofCallgraph for the view. + * + * Returns: (transfer none) (nullable): a #SysprofCallgraph or %NULL + */ +SysprofCallgraph * +sysprof_callgraph_view_get_callgraph (SysprofCallgraphView *self) +{ + g_return_val_if_fail (SYSPROF_IS_CALLGRAPH_VIEW (self), NULL); + + return self->callgraph; +} + +/** + * sysprof_callgraph_view_set_callgraph: + * @self: a #SysprofCallgraphView + * @callgraph: the #SysprofCallgraph + * + * Sets the callgraph for the view. + */ +void +sysprof_callgraph_view_set_callgraph (SysprofCallgraphView *self, + SysprofCallgraph *callgraph) +{ + g_return_if_fail (SYSPROF_IS_CALLGRAPH_VIEW (self)); + g_return_if_fail (SYSPROF_IS_CALLGRAPH (callgraph)); + + if (g_set_object (&self->callgraph, callgraph)) + { + g_autoptr(GtkMultiSelection) model = NULL; + + if (callgraph != NULL) + { + g_autoptr(GtkTreeListRowSorter) sorter = NULL; + g_autoptr(GtkSortListModel) sort_model = NULL; + g_autoptr(GtkTreeListModel) tree = NULL; + GtkSorter *column_sorter; + + tree = gtk_tree_list_model_new (g_object_ref (G_LIST_MODEL (callgraph)), + FALSE, + FALSE, + sysprof_callgraph_view_create_model_func, + NULL, + NULL); + + column_sorter = gtk_column_view_get_sorter (self->column_view); + sorter = gtk_tree_list_row_sorter_new (g_object_ref (column_sorter)); + sort_model = gtk_sort_list_model_new (g_object_ref (G_LIST_MODEL (tree)), g_object_ref (GTK_SORTER (sorter))); + model = gtk_multi_selection_new (g_object_ref (G_LIST_MODEL (sort_model))); + } + + gtk_column_view_set_model (self->column_view, GTK_SELECTION_MODEL (model)); + + g_object_notify_by_pspec (G_OBJECT (callgraph), properties[PROP_CALLGRAPH]); + } +} +#endif diff --git a/src/libsysprof-gtk/sysprof-callgraph-view.h b/src/libsysprof-gtk/sysprof-callgraph-view.h new file mode 100644 index 00000000..f61de95e --- /dev/null +++ b/src/libsysprof-gtk/sysprof-callgraph-view.h @@ -0,0 +1,53 @@ +/* sysprof-callgraph-view.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +#include +#include + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_CALLGRAPH_VIEW (sysprof_callgraph_view_get_type()) +#define SYSPROF_IS_CALLGRAPH_VIEW(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_CALLGRAPH_VIEW) +#define SYSPROF_CALLGRAPH_VIEW(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_CALLGRAPH_VIEW, SysprofCallgraphView) +#define SYSPROF_CALLGRAPH_VIEW_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_CALLGRAPH_VIEW, SysprofCallgraphViewClass) + +typedef struct _SysprofCallgraphView SysprofCallgraphView; +typedef struct _SysprofCallgraphViewClass SysprofCallgraphViewClass; + +SYSPROF_AVAILABLE_IN_ALL +GType sysprof_callgraph_view_get_type (void) G_GNUC_CONST; +SYSPROF_AVAILABLE_IN_ALL +SysprofDocument *sysprof_callgraph_view_get_document (SysprofCallgraphView *self); +SYSPROF_AVAILABLE_IN_ALL +void sysprof_callgraph_view_set_document (SysprofCallgraphView *self, + SysprofDocument *document); +SYSPROF_AVAILABLE_IN_ALL +GListModel *sysprof_callgraph_view_get_traceables (SysprofCallgraphView *self); +SYSPROF_AVAILABLE_IN_ALL +void sysprof_callgraph_view_set_traceables (SysprofCallgraphView *self, + GListModel *model); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofCallgraphView, g_object_unref) + +G_END_DECLS diff --git a/src/libsysprof-gtk/sysprof-callgraph-view.ui b/src/libsysprof-gtk/sysprof-callgraph-view.ui new file mode 100644 index 00000000..97d5b5a1 --- /dev/null +++ b/src/libsysprof-gtk/sysprof-callgraph-view.ui @@ -0,0 +1,90 @@ + + + + diff --git a/src/libsysprof-gtk/sysprof-gtk.h b/src/libsysprof-gtk/sysprof-gtk.h new file mode 100644 index 00000000..31e1cd5f --- /dev/null +++ b/src/libsysprof-gtk/sysprof-gtk.h @@ -0,0 +1,30 @@ +/* sysprof-gtk.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +G_BEGIN_DECLS + +#define SYSPROF_GTK_INSIDE +# include "sysprof-callgraph-view.h" +# include "sysprof-weighted-callgraph-view.h" +#undef SYSPROF_GTK_INSIDE + +G_END_DECLS diff --git a/src/libsysprof-gtk/sysprof-weighted-callgraph-view.c b/src/libsysprof-gtk/sysprof-weighted-callgraph-view.c new file mode 100644 index 00000000..8a7198fe --- /dev/null +++ b/src/libsysprof-gtk/sysprof-weighted-callgraph-view.c @@ -0,0 +1,85 @@ +/* sysprof-weighted-callgraph-view.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-callgraph-view-private.h" +#include "sysprof-weighted-callgraph-view.h" + +struct _SysprofWeightedCallgraphView +{ + SysprofCallgraphView parent_instance; +}; + +struct _SysprofWeightedCallgraphViewClass +{ + SysprofCallgraphViewClass parent_class; +}; + +typedef struct _AugmentWeight +{ + guint size; + guint total; +} AugmentWeight; + +G_DEFINE_FINAL_TYPE (SysprofWeightedCallgraphView, sysprof_weighted_callgraph_view, SYSPROF_TYPE_CALLGRAPH_VIEW) + +static void +augment_weight (SysprofCallgraph *callgraph, + SysprofCallgraphNode *node, + SysprofDocumentFrame *frame, + gpointer user_data) +{ + AugmentWeight *aug; + + g_assert (SYSPROF_IS_CALLGRAPH (callgraph)); + g_assert (node != NULL); + g_assert (SYSPROF_IS_DOCUMENT_SAMPLE (frame)); + g_assert (user_data == NULL); + + aug = sysprof_callgraph_get_augment (callgraph, node); + aug->size += 1; + + for (; node; node = sysprof_callgraph_node_parent (node)) + { + aug = sysprof_callgraph_get_augment (callgraph, node); + aug->total += 1; + } +} + +static void +sysprof_weighted_callgraph_view_class_init (SysprofWeightedCallgraphViewClass *klass) +{ + SysprofCallgraphViewClass *callgraph_view_class = SYSPROF_CALLGRAPH_VIEW_CLASS (klass); + + callgraph_view_class->augment_size = sizeof (AugmentWeight); + callgraph_view_class->augment_func = augment_weight; +} + +static void +sysprof_weighted_callgraph_view_init (SysprofWeightedCallgraphView *self) +{ +} + +GtkWidget * +sysprof_weighted_callgraph_view_new (void) +{ + return g_object_new (SYSPROF_TYPE_WEIGHTED_CALLGRAPH_VIEW, NULL); +} diff --git a/src/libsysprof-gtk/sysprof-weighted-callgraph-view.h b/src/libsysprof-gtk/sysprof-weighted-callgraph-view.h new file mode 100644 index 00000000..51297a4c --- /dev/null +++ b/src/libsysprof-gtk/sysprof-weighted-callgraph-view.h @@ -0,0 +1,42 @@ +/* sysprof-weighted-callgraph-view.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-callgraph-view.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_WEIGHTED_CALLGRAPH_VIEW (sysprof_weighted_callgraph_view_get_type()) +#define SYSPROF_IS_WEIGHTED_CALLGRAPH_VIEW(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_WEIGHTED_CALLGRAPH_VIEW) +#define SYSPROF_WEIGHTED_CALLGRAPH_VIEW(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_WEIGHTED_CALLGRAPH_VIEW, SysprofWeightedCallgraphView) +#define SYSPROF_WEIGHTED_CALLGRAPH_VIEW_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_WEIGHTED_CALLGRAPH_VIEW, SysprofWeightedCallgraphViewClass) + +typedef struct _SysprofWeightedCallgraphView SysprofWeightedCallgraphView; +typedef struct _SysprofWeightedCallgraphViewClass SysprofWeightedCallgraphViewClass; + +SYSPROF_AVAILABLE_IN_ALL +GType sysprof_weighted_callgraph_view_get_type (void) G_GNUC_CONST; +SYSPROF_AVAILABLE_IN_ALL +GtkWidget *sysprof_weighted_weighted_callgraph_view_new (void); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofWeightedCallgraphView, g_object_unref) + +G_END_DECLS diff --git a/src/libsysprof-gtk/tests/meson.build b/src/libsysprof-gtk/tests/meson.build new file mode 100644 index 00000000..8c3c93e2 --- /dev/null +++ b/src/libsysprof-gtk/tests/meson.build @@ -0,0 +1,31 @@ +libsysprof_gtk_test_env = [ + 'G_DEBUG=gc-friendly', + 'GSETTINGS_BACKEND=memory', + 'MALLOC_CHECK_=2', +] + +libsysprof_gtk_testsuite_c_args = [ + '-DG_LOG_DOMAIN="libdex"', + '-DG_ENABLE_DEBUG', + '-UG_DISABLE_ASSERT', + '-UG_DISABLE_CAST_CHECKS', +] + +libsysprof_gtk_testsuite = { + 'test-callgraph' : {'skip': true}, +} + +libsysprof_gtk_testsuite_deps = [ + libsysprof_analyze_static_dep, + libsysprof_gtk_static_dep, +] + +foreach test, params: libsysprof_gtk_testsuite + test_exe = executable(test, '@0@.c'.format(test), + c_args: libsysprof_gtk_testsuite_c_args, + dependencies: libsysprof_gtk_testsuite_deps, + ) + if not params.get('skip', false) + test(test, test_exe, env: libsysprof_gtk_test_env) + endif +endforeach diff --git a/src/libsysprof-gtk/tests/test-callgraph.c b/src/libsysprof-gtk/tests/test-callgraph.c new file mode 100644 index 00000000..40376362 --- /dev/null +++ b/src/libsysprof-gtk/tests/test-callgraph.c @@ -0,0 +1,112 @@ +/* test-callgraph.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include +#include +#include + +static GMainLoop *main_loop; +static char *kallsyms_path; +static char *filename; +static const GOptionEntry entries[] = { + { "kallsyms", 'k', 0, G_OPTION_ARG_FILENAME, &kallsyms_path, "The path to kallsyms to use for decoding", "PATH" }, + { 0 } +}; + +int +main (int argc, + char *argv[]) +{ + g_autoptr(GOptionContext) context = g_option_context_new ("- show a callgraph"); + g_autoptr(SysprofDocumentLoader) loader = NULL; + g_autoptr(SysprofDocument) document = NULL; + g_autoptr(SysprofMultiSymbolizer) multi = NULL; + g_autoptr(GListModel) model = NULL; + g_autoptr(GError) error = NULL; + SysprofCallgraphView *view; + GtkWindow *window; + + gtk_init (); + sysprof_clock_init (); + + g_option_context_add_main_entries (context, entries, NULL); + if (!g_option_context_parse (context, &argc, &argv, &error)) + { + g_printerr ("%s\n", error->message); + return 1; + } + + if (argc != 2) + { + g_print ("usage: %s [OPTIONS] CAPTURE_FILE\n", argv[0]); + return 1; + } + + filename = argv[1]; + + main_loop = g_main_loop_new (NULL, FALSE); + + multi = sysprof_multi_symbolizer_new (); + + if (kallsyms_path) + { + g_autoptr(GFile) kallsyms_file = g_file_new_for_path (kallsyms_path); + GFileInputStream *kallsyms_stream = g_file_read (kallsyms_file, NULL, NULL); + + sysprof_multi_symbolizer_take (multi, sysprof_kallsyms_symbolizer_new_for_symbols (G_INPUT_STREAM (kallsyms_stream))); + } + else + { + 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 ()); + + loader = sysprof_document_loader_new (filename); + sysprof_document_loader_set_symbolizer (loader, SYSPROF_SYMBOLIZER (multi)); + + g_print ("Loading %s, ignoring embedded symbols...\n", filename); + if (!(document = sysprof_document_loader_load (loader, NULL, &error))) + g_error ("Failed to load document: %s", error->message); + + model = sysprof_document_list_samples (document); + + window = g_object_new (GTK_TYPE_WINDOW, + "default-width", 800, + "default-height", 600, + NULL); + view = g_object_new (SYSPROF_TYPE_WEIGHTED_CALLGRAPH_VIEW, + "traceables", model, + "document", document, + NULL); + g_signal_connect_swapped (window, + "close-request", + G_CALLBACK (g_main_loop_quit), + main_loop); + gtk_window_set_child (window, GTK_WIDGET (view)); + gtk_window_present (window); + + g_main_loop_run (main_loop); + + return 0; +} diff --git a/src/meson.build b/src/meson.build index 616fcbf8..a2a841c5 100644 --- a/src/meson.build +++ b/src/meson.build @@ -75,6 +75,7 @@ if get_option('libsysprof') or get_option('agent') endif if get_option('gtk') and get_option('libsysprof') subdir('libsysprof-ui') + subdir('libsysprof-gtk') endif if get_option('gtk') and get_option('libsysprof') subdir('sysprof') From 7ec0b8f3a321f2eb2d15e89e4a42e6cfb7969dff Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 8 Jun 2023 17:57:44 -0700 Subject: [PATCH 0372/1030] libsysprof-gtk: add tooltip for callgraph view --- src/libsysprof-gtk/sysprof-callgraph-view.ui | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/libsysprof-gtk/sysprof-callgraph-view.ui b/src/libsysprof-gtk/sysprof-callgraph-view.ui index 97d5b5a1..54ea7d97 100644 --- a/src/libsysprof-gtk/sysprof-callgraph-view.ui +++ b/src/libsysprof-gtk/sysprof-callgraph-view.ui @@ -32,6 +32,14 @@ true 6 + true + + + + expander + + + start From 900c39c0d8294969af1970902c27df65749d9a74 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 9 Jun 2023 10:50:39 -0700 Subject: [PATCH 0373/1030] libsysprof-analyze: use summary struct for symbol information We still need to figure out the right way to plumb in the augmentation data so that things that need to walk up the chain are possible, but this first gets things in order to do that. --- src/libsysprof-analyze/sysprof-callgraph.c | 102 +++++++++++++-------- 1 file changed, 66 insertions(+), 36 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-callgraph.c b/src/libsysprof-analyze/sysprof-callgraph.c index 927b7ee3..1725968d 100644 --- a/src/libsysprof-analyze/sysprof-callgraph.c +++ b/src/libsysprof-analyze/sysprof-callgraph.c @@ -40,9 +40,7 @@ struct _SysprofCallgraph SysprofSymbol *everything; - GHashTable *symbol_to_bitset; - - GHashTable *callers; + GHashTable *symbol_to_summary; gsize augment_size; SysprofAugmentationFunc augment_func; @@ -52,6 +50,13 @@ struct _SysprofCallgraph SysprofCallgraphNode root; }; +typedef struct _SysprofCallgraphSummary +{ + gpointer augment; + EggBitset *traceables; + GPtrArray *callers; +} SysprofCallgraphSummary; + static GType sysprof_callgraph_get_item_type (GListModel *model) { @@ -87,6 +92,43 @@ list_model_iface_init (GListModelInterface *iface) G_DEFINE_FINAL_TYPE_WITH_CODE (SysprofCallgraph, sysprof_callgraph, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init)) +static void +sysprof_callgraph_summary_free_all (SysprofCallgraphSummary *summary) +{ + g_clear_pointer (&summary->augment, g_free); + g_clear_pointer (&summary->callers, g_ptr_array_unref); + g_clear_pointer (&summary->traceables, egg_bitset_unref); + g_free (summary); +} + +static void +sysprof_callgraph_summary_free_self (SysprofCallgraphSummary *summary) +{ + summary->augment = NULL; + g_clear_pointer (&summary->callers, g_ptr_array_unref); + g_clear_pointer (&summary->traceables, egg_bitset_unref); + g_free (summary); +} + +static inline SysprofCallgraphSummary * +sysprof_callgraph_get_summary (SysprofCallgraph *self, + SysprofSymbol *symbol) +{ + SysprofCallgraphSummary *summary; + + if G_UNLIKELY (!(summary = g_hash_table_lookup (self->symbol_to_summary, symbol))) + { + summary = g_new0 (SysprofCallgraphSummary, 1); + summary->augment = NULL; + summary->traceables = egg_bitset_new_empty (); + summary->callers = g_ptr_array_new (); + + g_hash_table_insert (self->symbol_to_summary, symbol, summary); + } + + return summary; +} + static void sysprof_callgraph_node_free (SysprofCallgraphNode *node, gboolean free_self) @@ -127,8 +169,7 @@ sysprof_callgraph_finalize (GObject *object) { SysprofCallgraph *self = (SysprofCallgraph *)object; - g_clear_pointer (&self->callers, g_hash_table_unref); - g_clear_pointer (&self->symbol_to_bitset, g_hash_table_unref); + g_clear_pointer (&self->symbol_to_summary, g_hash_table_unref); g_clear_object (&self->document); g_clear_object (&self->traceables); @@ -152,8 +193,6 @@ static void sysprof_callgraph_init (SysprofCallgraph *self) { self->everything = _sysprof_symbol_new (g_ref_string_new_intern ("[Everything]"), NULL, NULL, 0, 0); - self->callers = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_ptr_array_unref); - self->symbol_to_bitset = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)egg_bitset_unref); self->root.symbol = self->everything; } @@ -162,39 +201,22 @@ sysprof_callgraph_populate_callers (SysprofCallgraph *self, SysprofCallgraphNode *node, guint list_model_index) { - GHashTable *hash; - g_assert (SYSPROF_IS_CALLGRAPH (self)); g_assert (node != NULL); - hash = self->callers; - for (const SysprofCallgraphNode *iter = node; iter != NULL && iter->parent != NULL; iter = iter->parent) { - GPtrArray *callers; - EggBitset *bitset; + SysprofCallgraphSummary *summary; guint pos; - if (!(callers = g_hash_table_lookup (hash, iter->symbol))) - { - callers = g_ptr_array_new (); - g_hash_table_insert (hash, iter->symbol, callers); - } + summary = g_hash_table_lookup (self->symbol_to_summary, iter->symbol); - if (!(bitset = g_hash_table_lookup (self->symbol_to_bitset, iter->symbol))) - { - bitset = egg_bitset_new_empty (); - g_hash_table_insert (self->symbol_to_bitset, iter->symbol, bitset); - } + egg_bitset_add (summary->traceables, list_model_index); - egg_bitset_add (bitset, list_model_index); - - g_assert (iter->parent->symbol != NULL); - - if (!g_ptr_array_find (callers, iter->parent->symbol, &pos)) - g_ptr_array_add (callers, iter->parent->symbol); + if (!g_ptr_array_find (summary->callers, iter->parent->symbol, &pos)) + g_ptr_array_add (summary->callers, iter->parent->symbol); } } @@ -350,11 +372,17 @@ _sysprof_callgraph_new_async (SysprofDocument *document, { g_autoptr(SysprofCallgraph) self = NULL; g_autoptr(GTask) task = NULL; + GDestroyNotify summary_free; g_return_if_fail (SYSPROF_IS_DOCUMENT (document)); g_return_if_fail (G_IS_LIST_MODEL (traceables)); g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); + if (augment_size > GLIB_SIZEOF_VOID_P) + summary_free = (GDestroyNotify)sysprof_callgraph_summary_free_all; + else + summary_free = (GDestroyNotify)sysprof_callgraph_summary_free_self; + self = g_object_new (SYSPROF_TYPE_CALLGRAPH, NULL); self->document = g_object_ref (document); self->traceables = g_object_ref (traceables); @@ -362,6 +390,7 @@ _sysprof_callgraph_new_async (SysprofDocument *document, self->augment_func = augment_func; self->augment_func_data = augment_func_data; self->augment_func_data_destroy = augment_func_data_destroy; + self->symbol_to_summary = g_hash_table_new_full (NULL, NULL, NULL, summary_free); task = g_task_new (NULL, cancellable, callback, user_data); g_task_set_source_tag (task, _sysprof_callgraph_new_async); @@ -384,7 +413,8 @@ sysprof_callgraph_get_augment (SysprofCallgraph *callgraph, { if (callgraph->augment_size == 0) return NULL; - else if (callgraph->augment_size <= GLIB_SIZEOF_VOID_P) + + if (callgraph->augment_size <= GLIB_SIZEOF_VOID_P) return &node->augment; if (node->augment == NULL) @@ -412,9 +442,9 @@ GListModel * sysprof_callgraph_list_callers (SysprofCallgraph *self, SysprofCallgraphFrame *frame) { + SysprofCallgraphSummary *summary; SysprofSymbol *symbol; GListStore *store; - GPtrArray *callers; g_return_val_if_fail (SYSPROF_IS_CALLGRAPH (self), NULL); g_return_val_if_fail (SYSPROF_IS_CALLGRAPH_FRAME (frame), NULL); @@ -422,8 +452,8 @@ sysprof_callgraph_list_callers (SysprofCallgraph *self, store = g_list_store_new (SYSPROF_TYPE_SYMBOL); symbol = sysprof_callgraph_frame_get_symbol (frame); - if ((callers = g_hash_table_lookup (self->callers, symbol))) - g_list_store_splice (store, 0, 0, callers->pdata, callers->len); + if ((summary = g_hash_table_lookup (self->symbol_to_summary, symbol))) + g_list_store_splice (store, 0, 0, summary->callers->pdata, summary->callers->len); return G_LIST_MODEL (store); } @@ -442,13 +472,13 @@ GListModel * sysprof_callgraph_list_traceables_for_symbol (SysprofCallgraph *self, SysprofSymbol *symbol) { - EggBitset *bitset; + SysprofCallgraphSummary *summary; g_return_val_if_fail (SYSPROF_IS_CALLGRAPH (self), NULL); g_return_val_if_fail (SYSPROF_IS_SYMBOL (symbol), NULL); - if ((bitset = g_hash_table_lookup (self->symbol_to_bitset, symbol))) - return _sysprof_document_bitset_index_new (self->traceables, bitset); + if ((summary = g_hash_table_lookup (self->symbol_to_summary, symbol))) + return _sysprof_document_bitset_index_new (self->traceables, summary->traceables); return G_LIST_MODEL (g_list_store_new (SYSPROF_TYPE_DOCUMENT_TRACEABLE)); } From 814c25d45a6a264ecfddecfd75b0283ba4003130 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 9 Jun 2023 11:19:40 -0700 Subject: [PATCH 0374/1030] libsysprof-analyze: add summary augmentation capabilities This allows us to have augmentation at the summary level so that things like the weighted callgraph or memprof callgraph can add their respective summary data up the chain of function calls. --- .../sysprof-callgraph-frame.c | 22 +++++- .../sysprof-callgraph-private.h | 22 ++++-- src/libsysprof-analyze/sysprof-callgraph.c | 71 ++++++++++--------- src/libsysprof-analyze/sysprof-callgraph.h | 3 + 4 files changed, 79 insertions(+), 39 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-callgraph-frame.c b/src/libsysprof-analyze/sysprof-callgraph-frame.c index b98a94d4..a82ec139 100644 --- a/src/libsysprof-analyze/sysprof-callgraph-frame.c +++ b/src/libsysprof-analyze/sysprof-callgraph-frame.c @@ -176,7 +176,7 @@ sysprof_callgraph_frame_get_symbol (SysprofCallgraphFrame *self) if (self->callgraph == NULL) return NULL; - return self->node->symbol; + return self->node->summary->symbol; } /** @@ -197,3 +197,23 @@ sysprof_callgraph_frame_get_augment (SysprofCallgraphFrame *self) return sysprof_callgraph_get_augment (self->callgraph, self->node); } + +/** + * sysprof_callgraph_frame_get_summary_augment: (skip) + * @self: a #SysprofCallgraphFrame + * + * Gets the augmentation that was attached to the summary for + * the callgraph node's symbol. + * + * Returns: (nullable) (transfer none): the augmentation data + */ +gpointer +sysprof_callgraph_frame_get_summary_augment (SysprofCallgraphFrame *self) +{ + g_return_val_if_fail (SYSPROF_IS_CALLGRAPH_FRAME (self), NULL); + + if (self->callgraph == NULL) + return NULL; + + return sysprof_callgraph_get_summary_augment (self->callgraph, self->node); +} diff --git a/src/libsysprof-analyze/sysprof-callgraph-private.h b/src/libsysprof-analyze/sysprof-callgraph-private.h index 0765ee2e..c2eb7c52 100644 --- a/src/libsysprof-analyze/sysprof-callgraph-private.h +++ b/src/libsysprof-analyze/sysprof-callgraph-private.h @@ -25,16 +25,26 @@ #include "sysprof-callgraph.h" #include "sysprof-document.h" +#include "eggbitset.h" + G_BEGIN_DECLS +typedef struct _SysprofCallgraphSummary +{ + SysprofSymbol *symbol; + EggBitset *traceables; + GPtrArray *callers; + gpointer augment; +} SysprofCallgraphSummary; + struct _SysprofCallgraphNode { - SysprofCallgraphNode *parent; - SysprofCallgraphNode *prev; - SysprofCallgraphNode *next; - SysprofCallgraphNode *children; - SysprofSymbol *symbol; - gpointer augment; + SysprofCallgraphNode *parent; + SysprofCallgraphNode *prev; + SysprofCallgraphNode *next; + SysprofCallgraphNode *children; + SysprofCallgraphSummary *summary; + gpointer augment; }; void _sysprof_callgraph_new_async (SysprofDocument *document, diff --git a/src/libsysprof-analyze/sysprof-callgraph.c b/src/libsysprof-analyze/sysprof-callgraph.c index 1725968d..1d521329 100644 --- a/src/libsysprof-analyze/sysprof-callgraph.c +++ b/src/libsysprof-analyze/sysprof-callgraph.c @@ -38,8 +38,6 @@ struct _SysprofCallgraph SysprofDocument *document; GListModel *traceables; - SysprofSymbol *everything; - GHashTable *symbol_to_summary; gsize augment_size; @@ -50,13 +48,6 @@ struct _SysprofCallgraph SysprofCallgraphNode root; }; -typedef struct _SysprofCallgraphSummary -{ - gpointer augment; - EggBitset *traceables; - GPtrArray *callers; -} SysprofCallgraphSummary; - static GType sysprof_callgraph_get_item_type (GListModel *model) { @@ -92,6 +83,8 @@ list_model_iface_init (GListModelInterface *iface) G_DEFINE_FINAL_TYPE_WITH_CODE (SysprofCallgraph, sysprof_callgraph, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init)) +static SysprofSymbol *everything; + static void sysprof_callgraph_summary_free_all (SysprofCallgraphSummary *summary) { @@ -122,6 +115,7 @@ sysprof_callgraph_get_summary (SysprofCallgraph *self, summary->augment = NULL; summary->traceables = egg_bitset_new_empty (); summary->callers = g_ptr_array_new (); + summary->symbol = symbol; g_hash_table_insert (self->symbol_to_summary, symbol, summary); } @@ -173,7 +167,6 @@ sysprof_callgraph_finalize (GObject *object) g_clear_object (&self->document); g_clear_object (&self->traceables); - g_clear_object (&self->everything); sysprof_callgraph_node_free (&self->root, FALSE); @@ -187,13 +180,13 @@ sysprof_callgraph_class_init (SysprofCallgraphClass *klass) object_class->dispose = sysprof_callgraph_dispose; object_class->finalize = sysprof_callgraph_finalize; + + everything = _sysprof_symbol_new (g_ref_string_new_intern ("[Everything]"), NULL, NULL, 0, 0); } static void sysprof_callgraph_init (SysprofCallgraph *self) { - self->everything = _sysprof_symbol_new (g_ref_string_new_intern ("[Everything]"), NULL, NULL, 0, 0); - self->root.symbol = self->everything; } static void @@ -208,15 +201,13 @@ sysprof_callgraph_populate_callers (SysprofCallgraph *self, iter != NULL && iter->parent != NULL; iter = iter->parent) { - SysprofCallgraphSummary *summary; + SysprofSymbol *parent_symbol = iter->parent->summary->symbol; guint pos; - summary = g_hash_table_lookup (self->symbol_to_summary, iter->symbol); + egg_bitset_add (iter->summary->traceables, list_model_index); - egg_bitset_add (summary->traceables, list_model_index); - - if (!g_ptr_array_find (summary->callers, iter->parent->symbol, &pos)) - g_ptr_array_add (summary->callers, iter->parent->symbol); + if (!g_ptr_array_find (iter->summary->callers, parent_symbol, &pos)) + g_ptr_array_add (iter->summary->callers, parent_symbol); } } @@ -230,7 +221,7 @@ sysprof_callgraph_add_trace (SysprofCallgraph *self, g_assert (SYSPROF_IS_CALLGRAPH (self)); g_assert (n_symbols >= 2); - g_assert (symbols[n_symbols-1] == self->everything); + g_assert (symbols[n_symbols-1] == everything); parent = &self->root; @@ -255,10 +246,11 @@ sysprof_callgraph_add_trace (SysprofCallgraph *self, iter = iter->next) { g_assert (iter != NULL); - g_assert (iter->symbol != NULL); + g_assert (iter->summary != NULL); + g_assert (iter->summary->symbol != NULL); g_assert (symbol != NULL); - if (_sysprof_symbol_equal (iter->symbol, symbol)) + if (_sysprof_symbol_equal (iter->summary->symbol, symbol)) { node = iter; goto next_symbol; @@ -267,7 +259,7 @@ sysprof_callgraph_add_trace (SysprofCallgraph *self, /* Otherwise create a new node */ node = g_new0 (SysprofCallgraphNode, 1); - node->symbol = symbol; + node->summary = sysprof_callgraph_get_summary (self, symbol); node->parent = parent; node->next = parent->children; if (parent->children) @@ -322,7 +314,7 @@ sysprof_callgraph_add_traceable (SysprofCallgraph *self, if (final_context == SYSPROF_ADDRESS_CONTEXT_KERNEL) symbols[n_symbols++] = _sysprof_document_kernel_symbol (self->document); symbols[n_symbols++] = _sysprof_document_process_symbol (self->document, pid); - symbols[n_symbols++] = self->everything; + symbols[n_symbols++] = everything; node = sysprof_callgraph_add_trace (self, symbols, n_symbols, list_model_index); @@ -391,6 +383,7 @@ _sysprof_callgraph_new_async (SysprofDocument *document, self->augment_func_data = augment_func_data; self->augment_func_data_destroy = augment_func_data_destroy; self->symbol_to_summary = g_hash_table_new_full (NULL, NULL, NULL, summary_free); + self->root.summary = sysprof_callgraph_get_summary (self, everything); task = g_task_new (NULL, cancellable, callback, user_data); g_task_set_source_tag (task, _sysprof_callgraph_new_async); @@ -407,20 +400,34 @@ _sysprof_callgraph_new_finish (GAsyncResult *result, return g_task_propagate_pointer (G_TASK (result), error); } -gpointer -sysprof_callgraph_get_augment (SysprofCallgraph *callgraph, - SysprofCallgraphNode *node) +static inline gpointer +get_augmentation (SysprofCallgraph *self, + gpointer *augment_location) { - if (callgraph->augment_size == 0) + if (self->augment_size == 0) return NULL; - if (callgraph->augment_size <= GLIB_SIZEOF_VOID_P) - return &node->augment; + if (self->augment_size <= GLIB_SIZEOF_VOID_P) + return augment_location; - if (node->augment == NULL) - node->augment = g_malloc0 (callgraph->augment_size); + if (*augment_location == NULL) + *augment_location = g_malloc0 (self->augment_size); - return node->augment; + return *augment_location; +} + +gpointer +sysprof_callgraph_get_augment (SysprofCallgraph *self, + SysprofCallgraphNode *node) +{ + return get_augmentation (self, &node->augment); +} + +gpointer +sysprof_callgraph_get_summary_augment (SysprofCallgraph *self, + SysprofCallgraphNode *node) +{ + return get_augmentation (self, &node->summary->augment); } SysprofCallgraphNode * diff --git a/src/libsysprof-analyze/sysprof-callgraph.h b/src/libsysprof-analyze/sysprof-callgraph.h index 97355731..5e425712 100644 --- a/src/libsysprof-analyze/sysprof-callgraph.h +++ b/src/libsysprof-analyze/sysprof-callgraph.h @@ -66,6 +66,9 @@ SYSPROF_AVAILABLE_IN_ALL gpointer sysprof_callgraph_get_augment (SysprofCallgraph *self, SysprofCallgraphNode *node); SYSPROF_AVAILABLE_IN_ALL +gpointer sysprof_callgraph_get_summary_augment (SysprofCallgraph *self, + SysprofCallgraphNode *node); +SYSPROF_AVAILABLE_IN_ALL SysprofCallgraphNode *sysprof_callgraph_node_parent (SysprofCallgraphNode *node); G_END_DECLS From f4eaf1ec51c584fa4e053c1450b6f641bd0da0f0 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 9 Jun 2023 11:22:51 -0700 Subject: [PATCH 0375/1030] libsysprof-gtk: add to weight to summary augment --- .../sysprof-weighted-callgraph-view.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/libsysprof-gtk/sysprof-weighted-callgraph-view.c b/src/libsysprof-gtk/sysprof-weighted-callgraph-view.c index 8a7198fe..230b302a 100644 --- a/src/libsysprof-gtk/sysprof-weighted-callgraph-view.c +++ b/src/libsysprof-gtk/sysprof-weighted-callgraph-view.c @@ -47,20 +47,27 @@ augment_weight (SysprofCallgraph *callgraph, SysprofDocumentFrame *frame, gpointer user_data) { - AugmentWeight *aug; + AugmentWeight *cur; + AugmentWeight *sum; g_assert (SYSPROF_IS_CALLGRAPH (callgraph)); g_assert (node != NULL); g_assert (SYSPROF_IS_DOCUMENT_SAMPLE (frame)); g_assert (user_data == NULL); - aug = sysprof_callgraph_get_augment (callgraph, node); - aug->size += 1; + cur = sysprof_callgraph_get_augment (callgraph, node); + cur->size += 1; + + sum = sysprof_callgraph_get_summary_augment (callgraph, node); + sum->size += 1; for (; node; node = sysprof_callgraph_node_parent (node)) { - aug = sysprof_callgraph_get_augment (callgraph, node); - aug->total += 1; + cur = sysprof_callgraph_get_augment (callgraph, node); + cur->total += 1; + + sum = sysprof_callgraph_get_summary_augment (callgraph, node); + sum->size += 1; } } From 95281e3cfb21fef35037ab5704ac7f8e1cd1e58d Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 9 Jun 2023 14:22:17 -0700 Subject: [PATCH 0376/1030] libsysprof-gtk: add css loading helper --- .../libsysprof-gtk.gresource.xml | 1 + src/libsysprof-gtk/meson.build | 1 + src/libsysprof-gtk/style.css | 26 +++++++++++++ src/libsysprof-gtk/sysprof-css-private.h | 29 ++++++++++++++ src/libsysprof-gtk/sysprof-css.c | 38 +++++++++++++++++++ 5 files changed, 95 insertions(+) create mode 100644 src/libsysprof-gtk/style.css create mode 100644 src/libsysprof-gtk/sysprof-css-private.h create mode 100644 src/libsysprof-gtk/sysprof-css.c diff --git a/src/libsysprof-gtk/libsysprof-gtk.gresource.xml b/src/libsysprof-gtk/libsysprof-gtk.gresource.xml index 2a36c015..c4a74e39 100644 --- a/src/libsysprof-gtk/libsysprof-gtk.gresource.xml +++ b/src/libsysprof-gtk/libsysprof-gtk.gresource.xml @@ -2,5 +2,6 @@ sysprof-callgraph-view.ui + style.css diff --git a/src/libsysprof-gtk/meson.build b/src/libsysprof-gtk/meson.build index 2c517f6e..d5948613 100644 --- a/src/libsysprof-gtk/meson.build +++ b/src/libsysprof-gtk/meson.build @@ -4,6 +4,7 @@ libsysprof_gtk_public_sources = [ ] libsysprof_gtk_private_sources = [ + 'sysprof-css.c', ] libsysprof_gtk_public_headers = [ diff --git a/src/libsysprof-gtk/style.css b/src/libsysprof-gtk/style.css new file mode 100644 index 00000000..a71f8c0d --- /dev/null +++ b/src/libsysprof-gtk/style.css @@ -0,0 +1,26 @@ +progresscell progress { + background: @accent_bg_color; + border-radius: 3px; + border: 1px solid shade(@accent_bg_color, .75); + border-right: none; +} + +progresscell progress:backdrop { + background: @borders; + border-color: shade(@borders, .9); +} + +progresscell trough { + background: alpha(@borders, .5); + border: 1px solid @borders; + border-radius: 3px; +} + +progresscell label { + font-size: 0.9em; + padding: 1px 3px; +} + +progresscell label.in-progress { + color: @accent_fg_color; +} diff --git a/src/libsysprof-gtk/sysprof-css-private.h b/src/libsysprof-gtk/sysprof-css-private.h new file mode 100644 index 00000000..608ced5b --- /dev/null +++ b/src/libsysprof-gtk/sysprof-css-private.h @@ -0,0 +1,29 @@ +/* sysprof-css-private.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +void _sysprof_css_init (void); + +G_END_DECLS diff --git a/src/libsysprof-gtk/sysprof-css.c b/src/libsysprof-gtk/sysprof-css.c new file mode 100644 index 00000000..464cadbe --- /dev/null +++ b/src/libsysprof-gtk/sysprof-css.c @@ -0,0 +1,38 @@ +/* sysprof-css.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-css-private.h" + +void +_sysprof_css_init (void) +{ + static GtkCssProvider *css; + + if (css == NULL) + { + css = gtk_css_provider_new (); + gtk_css_provider_load_from_resource (css, "/libsysprof-gtk/style.css"); + gtk_style_context_add_provider_for_display (gdk_display_get_default (), + GTK_STYLE_PROVIDER (css), + G_MAXUINT); + } +} From c97c4c0b0a77925dc300be55986afc7192ea764b Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 9 Jun 2023 14:23:01 -0700 Subject: [PATCH 0377/1030] libsysprof-gtk: add SysprofProgressCell And also, this library is meant to build on libadwaita, so require that as well going forward. --- src/libsysprof-gtk/meson.build | 2 + .../sysprof-progress-cell-private.h | 36 +++ src/libsysprof-gtk/sysprof-progress-cell.c | 270 ++++++++++++++++++ src/libsysprof-gtk/tests/test-callgraph.c | 5 +- 4 files changed, 312 insertions(+), 1 deletion(-) create mode 100644 src/libsysprof-gtk/sysprof-progress-cell-private.h create mode 100644 src/libsysprof-gtk/sysprof-progress-cell.c diff --git a/src/libsysprof-gtk/meson.build b/src/libsysprof-gtk/meson.build index d5948613..c79ba64a 100644 --- a/src/libsysprof-gtk/meson.build +++ b/src/libsysprof-gtk/meson.build @@ -5,6 +5,7 @@ libsysprof_gtk_public_sources = [ libsysprof_gtk_private_sources = [ 'sysprof-css.c', + 'sysprof-progress-cell.c', ] libsysprof_gtk_public_headers = [ @@ -15,6 +16,7 @@ libsysprof_gtk_public_headers = [ ] libsysprof_gtk_deps = [ + dependency('libadwaita-1'), dependency('gtk4', version: gtk_req_version), libsysprof_analyze_dep, diff --git a/src/libsysprof-gtk/sysprof-progress-cell-private.h b/src/libsysprof-gtk/sysprof-progress-cell-private.h new file mode 100644 index 00000000..ce9406a7 --- /dev/null +++ b/src/libsysprof-gtk/sysprof-progress-cell-private.h @@ -0,0 +1,36 @@ +/* sysprof-progress-cell.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_PROGRESS_CELL (sysprof_progress_cell_get_type()) + +G_DECLARE_FINAL_TYPE (SysprofProgressCell, sysprof_progress_cell, SYSPROF, PROGRESS_CELL, GtkWidget) + +GtkWidget *sysprof_progress_cell_new (void); +double sysprof_progress_cell_get_fraction (SysprofProgressCell *self); +void sysprof_progress_cell_set_fraction (SysprofProgressCell *self, + double fraction); + +G_END_DECLS diff --git a/src/libsysprof-gtk/sysprof-progress-cell.c b/src/libsysprof-gtk/sysprof-progress-cell.c new file mode 100644 index 00000000..103b26e9 --- /dev/null +++ b/src/libsysprof-gtk/sysprof-progress-cell.c @@ -0,0 +1,270 @@ +/* sysprof-progress-cell.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include + +#include "sysprof-css-private.h" +#include "sysprof-progress-cell-private.h" + +struct _SysprofProgressCell +{ + GtkWidget parent_instance; + + AdwBin *trough; + AdwBin *progress; + GtkLabel *label; + GtkLabel *alt_label; + + double fraction; +}; + +enum { + PROP_0, + PROP_FRACTION, + N_PROPS +}; + +G_DEFINE_FINAL_TYPE (SysprofProgressCell, sysprof_progress_cell, GTK_TYPE_WIDGET) + +static GParamSpec *properties [N_PROPS]; + +static void +sysprof_progress_cell_size_allocate (GtkWidget *widget, + int width, + int height, + int baseline) +{ + SysprofProgressCell *self = (SysprofProgressCell *)widget; + GtkAllocation all = {0, 0, width, height}; + + g_assert (SYSPROF_IS_PROGRESS_CELL (self)); + + gtk_widget_size_allocate (GTK_WIDGET (self->label), &all, baseline); + gtk_widget_size_allocate (GTK_WIDGET (self->alt_label), &all, baseline); + gtk_widget_size_allocate (GTK_WIDGET (self->trough), &all, baseline); + gtk_widget_size_allocate (GTK_WIDGET (self->progress), + &(const GtkAllocation) {0, 0, width*self->fraction, height}, + baseline); +} + +static void +sysprof_progress_cell_snapshot (GtkWidget *widget, + GtkSnapshot *snapshot) +{ + SysprofProgressCell *self = (SysprofProgressCell *)widget; + int w, h; + + g_assert (SYSPROF_IS_PROGRESS_CELL (self)); + + gtk_widget_snapshot_child (widget, GTK_WIDGET (self->trough), snapshot); + gtk_widget_snapshot_child (widget, GTK_WIDGET (self->label), snapshot); + gtk_widget_snapshot_child (widget, GTK_WIDGET (self->progress), snapshot); + + w = gtk_widget_get_width (widget); + h = gtk_widget_get_height (widget); + + gtk_snapshot_push_clip (snapshot, + &GRAPHENE_RECT_INIT (0, 0, w * self->fraction, h)); + gtk_widget_snapshot_child (widget, GTK_WIDGET (self->alt_label), snapshot); + gtk_snapshot_pop (snapshot); +} + +static void +sysprof_progress_cell_measure (GtkWidget *widget, + GtkOrientation orientation, + int for_size, + int *minimum, + int *natural, + int *minimum_baseline, + int *natural_baseline) +{ + SysprofProgressCell *self = SYSPROF_PROGRESS_CELL (widget); + GtkWidget *widgets[] = { + GTK_WIDGET (self->trough), + GTK_WIDGET (self->progress), + GTK_WIDGET (self->label), + GTK_WIDGET (self->alt_label), + }; + + *minimum = 0; + *natural = 0; + *minimum_baseline = -1; + *natural_baseline = -1; + + for (guint i = 0; i < G_N_ELEMENTS (widgets); i++) + { + int child_min, child_nat; + + gtk_widget_measure (widgets[i], orientation, for_size, &child_min, &child_nat, NULL, NULL); + + if (child_min > *minimum) + *minimum = child_min; + + if (child_nat > *natural) + *natural = child_nat; + } +} + +static void +sysprof_progress_cell_dispose (GObject *object) +{ + SysprofProgressCell *self = (SysprofProgressCell *)object; + + gtk_widget_unparent (GTK_WIDGET (self->trough)); + gtk_widget_unparent (GTK_WIDGET (self->progress)); + gtk_widget_unparent (GTK_WIDGET (self->label)); + gtk_widget_unparent (GTK_WIDGET (self->alt_label)); + + self->trough = NULL; + self->progress = NULL; + self->label = NULL; + self->alt_label = NULL; + + G_OBJECT_CLASS (sysprof_progress_cell_parent_class)->dispose (object); +} + +static void +sysprof_progress_cell_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofProgressCell *self = SYSPROF_PROGRESS_CELL (object); + + switch (prop_id) + { + case PROP_FRACTION: + g_value_set_double (value, sysprof_progress_cell_get_fraction (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_progress_cell_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SysprofProgressCell *self = SYSPROF_PROGRESS_CELL (object); + + switch (prop_id) + { + case PROP_FRACTION: + sysprof_progress_cell_set_fraction (self, g_value_get_double (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_progress_cell_class_init (SysprofProgressCellClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->dispose = sysprof_progress_cell_dispose; + object_class->get_property = sysprof_progress_cell_get_property; + object_class->set_property = sysprof_progress_cell_set_property; + + widget_class->size_allocate = sysprof_progress_cell_size_allocate; + widget_class->snapshot = sysprof_progress_cell_snapshot; + widget_class->measure = sysprof_progress_cell_measure; + + properties [PROP_FRACTION] = + g_param_spec_double ("fraction", NULL, NULL, + 0, 1, 0, + (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_css_name (widget_class, "progresscell"); +} + +static void +sysprof_progress_cell_init (SysprofProgressCell *self) +{ + _sysprof_css_init (); + + self->label = g_object_new (GTK_TYPE_LABEL, + "halign", GTK_ALIGN_CENTER, + "valign", GTK_ALIGN_CENTER, + NULL); + self->alt_label = g_object_new (GTK_TYPE_LABEL, + "halign", GTK_ALIGN_CENTER, + "valign", GTK_ALIGN_CENTER, + NULL); + gtk_widget_add_css_class (GTK_WIDGET (self->alt_label), "in-progress"); + self->trough = g_object_new (ADW_TYPE_BIN, + "css-name", "trough", + NULL); + self->progress = g_object_new (ADW_TYPE_BIN, + "css-name", "progress", + NULL); + + gtk_widget_set_parent (GTK_WIDGET (self->trough), GTK_WIDGET (self)); + gtk_widget_set_parent (GTK_WIDGET (self->label), GTK_WIDGET (self)); + gtk_widget_set_parent (GTK_WIDGET (self->progress), GTK_WIDGET (self)); + gtk_widget_set_parent (GTK_WIDGET (self->alt_label), GTK_WIDGET (self)); +} + +GtkWidget * +sysprof_progress_cell_new (void) +{ + return g_object_new (SYSPROF_TYPE_PROGRESS_CELL, NULL); +} + +double +sysprof_progress_cell_get_fraction (SysprofProgressCell *self) +{ + g_return_val_if_fail (SYSPROF_IS_PROGRESS_CELL (self), .0); + + return self->fraction; +} + +void +sysprof_progress_cell_set_fraction (SysprofProgressCell *self, + double fraction) +{ + g_return_if_fail (SYSPROF_IS_PROGRESS_CELL (self)); + + fraction = CLAMP (fraction, 0., 1.); + + if (fraction != self->fraction) + { + char text[32]; + + self->fraction = fraction; + + g_snprintf (text, sizeof text, "%.2lf%%", fraction*100.); + gtk_label_set_text (self->label, text); + gtk_label_set_text (self->alt_label, text); + + gtk_widget_queue_allocate (GTK_WIDGET (self)); + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FRACTION]); + } +} diff --git a/src/libsysprof-gtk/tests/test-callgraph.c b/src/libsysprof-gtk/tests/test-callgraph.c index 40376362..5a5837ec 100644 --- a/src/libsysprof-gtk/tests/test-callgraph.c +++ b/src/libsysprof-gtk/tests/test-callgraph.c @@ -20,6 +20,7 @@ #include "config.h" +#include #include #include #include @@ -45,9 +46,11 @@ main (int argc, SysprofCallgraphView *view; GtkWindow *window; - gtk_init (); sysprof_clock_init (); + gtk_init (); + adw_init (); + g_option_context_add_main_entries (context, entries, NULL); if (!g_option_context_parse (context, &argc, &argv, &error)) { From c5b5ae9df775723b9a0ae86d7149473218cc2a45 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 9 Jun 2023 14:23:25 -0700 Subject: [PATCH 0378/1030] libsysprof-gtk: add access to internal column_view child --- src/libsysprof-gtk/sysprof-callgraph-view.c | 70 +++++---------------- 1 file changed, 14 insertions(+), 56 deletions(-) diff --git a/src/libsysprof-gtk/sysprof-callgraph-view.c b/src/libsysprof-gtk/sysprof-callgraph-view.c index 7d2a5b23..f046d13c 100644 --- a/src/libsysprof-gtk/sysprof-callgraph-view.c +++ b/src/libsysprof-gtk/sysprof-callgraph-view.c @@ -31,7 +31,10 @@ enum { N_PROPS }; -G_DEFINE_ABSTRACT_TYPE (SysprofCallgraphView, sysprof_callgraph_view, GTK_TYPE_WIDGET) +static void buildable_iface_init (GtkBuildableIface *iface); + +G_DEFINE_ABSTRACT_TYPE_WITH_CODE (SysprofCallgraphView, sysprof_callgraph_view, GTK_TYPE_WIDGET, + G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, buildable_iface_init)) static GParamSpec *properties [N_PROPS]; @@ -312,64 +315,19 @@ sysprof_callgraph_view_set_traceables (SysprofCallgraphView *self, } } -#if 0 -/** - * sysprof_callgraph_view_get_callgraph: - * @self: a #SysprofCallgraphView - * - * Gets the #SysprofCallgraph for the view. - * - * Returns: (transfer none) (nullable): a #SysprofCallgraph or %NULL - */ -SysprofCallgraph * -sysprof_callgraph_view_get_callgraph (SysprofCallgraphView *self) +static GObject * +sysprof_callgraph_view_get_internal_child (GtkBuildable *buildable, + GtkBuilder *builder, + const char *name) { - g_return_val_if_fail (SYSPROF_IS_CALLGRAPH_VIEW (self), NULL); + if (g_strcmp0 (name, "column_view") == 0) + return G_OBJECT (SYSPROF_CALLGRAPH_VIEW (buildable)->column_view); - return self->callgraph; + return NULL; } -/** - * sysprof_callgraph_view_set_callgraph: - * @self: a #SysprofCallgraphView - * @callgraph: the #SysprofCallgraph - * - * Sets the callgraph for the view. - */ -void -sysprof_callgraph_view_set_callgraph (SysprofCallgraphView *self, - SysprofCallgraph *callgraph) +static void +buildable_iface_init (GtkBuildableIface *iface) { - g_return_if_fail (SYSPROF_IS_CALLGRAPH_VIEW (self)); - g_return_if_fail (SYSPROF_IS_CALLGRAPH (callgraph)); - - if (g_set_object (&self->callgraph, callgraph)) - { - g_autoptr(GtkMultiSelection) model = NULL; - - if (callgraph != NULL) - { - g_autoptr(GtkTreeListRowSorter) sorter = NULL; - g_autoptr(GtkSortListModel) sort_model = NULL; - g_autoptr(GtkTreeListModel) tree = NULL; - GtkSorter *column_sorter; - - tree = gtk_tree_list_model_new (g_object_ref (G_LIST_MODEL (callgraph)), - FALSE, - FALSE, - sysprof_callgraph_view_create_model_func, - NULL, - NULL); - - column_sorter = gtk_column_view_get_sorter (self->column_view); - sorter = gtk_tree_list_row_sorter_new (g_object_ref (column_sorter)); - sort_model = gtk_sort_list_model_new (g_object_ref (G_LIST_MODEL (tree)), g_object_ref (GTK_SORTER (sorter))); - model = gtk_multi_selection_new (g_object_ref (G_LIST_MODEL (sort_model))); - } - - gtk_column_view_set_model (self->column_view, GTK_SELECTION_MODEL (model)); - - g_object_notify_by_pspec (G_OBJECT (callgraph), properties[PROP_CALLGRAPH]); - } + iface->get_internal_child = sysprof_callgraph_view_get_internal_child; } -#endif From 626ccdce4da599615d815b8d39e3e782f5a27f06 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 9 Jun 2023 14:24:08 -0700 Subject: [PATCH 0379/1030] libsysprof-gtk: add columns to weighted callgraph view These don't actually do anything yet, however. --- .../libsysprof-gtk.gresource.xml | 1 + .../sysprof-weighted-callgraph-view.c | 12 +++++++ .../sysprof-weighted-callgraph-view.ui | 36 +++++++++++++++++++ 3 files changed, 49 insertions(+) create mode 100644 src/libsysprof-gtk/sysprof-weighted-callgraph-view.ui diff --git a/src/libsysprof-gtk/libsysprof-gtk.gresource.xml b/src/libsysprof-gtk/libsysprof-gtk.gresource.xml index c4a74e39..a7d26acf 100644 --- a/src/libsysprof-gtk/libsysprof-gtk.gresource.xml +++ b/src/libsysprof-gtk/libsysprof-gtk.gresource.xml @@ -2,6 +2,7 @@ sysprof-callgraph-view.ui + sysprof-weighted-callgraph-view.ui style.css diff --git a/src/libsysprof-gtk/sysprof-weighted-callgraph-view.c b/src/libsysprof-gtk/sysprof-weighted-callgraph-view.c index 230b302a..9a4227f6 100644 --- a/src/libsysprof-gtk/sysprof-weighted-callgraph-view.c +++ b/src/libsysprof-gtk/sysprof-weighted-callgraph-view.c @@ -22,10 +22,14 @@ #include "sysprof-callgraph-view-private.h" #include "sysprof-weighted-callgraph-view.h" +#include "sysprof-progress-cell-private.h" struct _SysprofWeightedCallgraphView { SysprofCallgraphView parent_instance; + + GtkColumnViewColumn *self_column; + GtkColumnViewColumn *total_column; }; struct _SysprofWeightedCallgraphViewClass @@ -75,14 +79,22 @@ static void sysprof_weighted_callgraph_view_class_init (SysprofWeightedCallgraphViewClass *klass) { SysprofCallgraphViewClass *callgraph_view_class = SYSPROF_CALLGRAPH_VIEW_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); callgraph_view_class->augment_size = sizeof (AugmentWeight); callgraph_view_class->augment_func = augment_weight; + + gtk_widget_class_set_template_from_resource (widget_class, "/libsysprof-gtk/sysprof-weighted-callgraph-view.ui"); + gtk_widget_class_bind_template_child (widget_class, SysprofWeightedCallgraphView, self_column); + gtk_widget_class_bind_template_child (widget_class, SysprofWeightedCallgraphView, total_column); + + g_type_ensure (SYSPROF_TYPE_PROGRESS_CELL); } static void sysprof_weighted_callgraph_view_init (SysprofWeightedCallgraphView *self) { + gtk_widget_init_template (GTK_WIDGET (self)); } GtkWidget * diff --git a/src/libsysprof-gtk/sysprof-weighted-callgraph-view.ui b/src/libsysprof-gtk/sysprof-weighted-callgraph-view.ui new file mode 100644 index 00000000..fc612f14 --- /dev/null +++ b/src/libsysprof-gtk/sysprof-weighted-callgraph-view.ui @@ -0,0 +1,36 @@ + + + + From 89173fdce7a4a36e06345e39ee00e3caf11fce99 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 9 Jun 2023 15:15:40 -0700 Subject: [PATCH 0380/1030] libsysprof-gtk: some progress style and text tweaks --- src/libsysprof-gtk/style.css | 5 +++++ src/libsysprof-gtk/sysprof-progress-cell.c | 22 ++++++++++++++++------ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/libsysprof-gtk/style.css b/src/libsysprof-gtk/style.css index a71f8c0d..18eb1adb 100644 --- a/src/libsysprof-gtk/style.css +++ b/src/libsysprof-gtk/style.css @@ -5,6 +5,10 @@ progresscell progress { border-right: none; } +progresscell { + min-height: 18px; +} + progresscell progress:backdrop { background: @borders; border-color: shade(@borders, .9); @@ -19,6 +23,7 @@ progresscell trough { progresscell label { font-size: 0.9em; padding: 1px 3px; + font-feature-settings: 'tnum'; } progresscell label.in-progress { diff --git a/src/libsysprof-gtk/sysprof-progress-cell.c b/src/libsysprof-gtk/sysprof-progress-cell.c index 103b26e9..7797211c 100644 --- a/src/libsysprof-gtk/sysprof-progress-cell.c +++ b/src/libsysprof-gtk/sysprof-progress-cell.c @@ -61,9 +61,11 @@ sysprof_progress_cell_size_allocate (GtkWidget *widget, gtk_widget_size_allocate (GTK_WIDGET (self->label), &all, baseline); gtk_widget_size_allocate (GTK_WIDGET (self->alt_label), &all, baseline); gtk_widget_size_allocate (GTK_WIDGET (self->trough), &all, baseline); - gtk_widget_size_allocate (GTK_WIDGET (self->progress), - &(const GtkAllocation) {0, 0, width*self->fraction, height}, - baseline); + + if (gtk_widget_get_visible (GTK_WIDGET (self->progress))) + gtk_widget_size_allocate (GTK_WIDGET (self->progress), + &(const GtkAllocation) {0, 0, MAX (1, width*self->fraction), height}, + baseline); } static void @@ -207,15 +209,21 @@ sysprof_progress_cell_class_init (SysprofProgressCellClass *klass) static void sysprof_progress_cell_init (SysprofProgressCell *self) { + char percent[32]; + _sysprof_css_init (); + g_snprintf (percent, sizeof percent, "%6.2lf%%", .0); + self->label = g_object_new (GTK_TYPE_LABEL, - "halign", GTK_ALIGN_CENTER, + "halign", GTK_ALIGN_END, "valign", GTK_ALIGN_CENTER, + "label", percent, NULL); self->alt_label = g_object_new (GTK_TYPE_LABEL, - "halign", GTK_ALIGN_CENTER, + "halign", GTK_ALIGN_END, "valign", GTK_ALIGN_CENTER, + "label", percent, NULL); gtk_widget_add_css_class (GTK_WIDGET (self->alt_label), "in-progress"); self->trough = g_object_new (ADW_TYPE_BIN, @@ -223,6 +231,7 @@ sysprof_progress_cell_init (SysprofProgressCell *self) NULL); self->progress = g_object_new (ADW_TYPE_BIN, "css-name", "progress", + "visible", FALSE, NULL); gtk_widget_set_parent (GTK_WIDGET (self->trough), GTK_WIDGET (self)); @@ -259,10 +268,11 @@ sysprof_progress_cell_set_fraction (SysprofProgressCell *self, self->fraction = fraction; - g_snprintf (text, sizeof text, "%.2lf%%", fraction*100.); + g_snprintf (text, sizeof text, "%6.2lf%%", fraction*100.); gtk_label_set_text (self->label, text); gtk_label_set_text (self->alt_label, text); + gtk_widget_set_visible (GTK_WIDGET (self->progress), fraction > .0); gtk_widget_queue_allocate (GTK_WIDGET (self)); g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FRACTION]); From c35f8d083daa6f01c84d7c7b1a662b61d33b6e69 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 9 Jun 2023 15:16:04 -0700 Subject: [PATCH 0381/1030] libsysprof-analyze: give frame access to callgraph via property --- src/libsysprof-analyze/sysprof-callgraph-frame.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-callgraph-frame.c b/src/libsysprof-analyze/sysprof-callgraph-frame.c index a82ec139..e9ef2a0e 100644 --- a/src/libsysprof-analyze/sysprof-callgraph-frame.c +++ b/src/libsysprof-analyze/sysprof-callgraph-frame.c @@ -35,6 +35,7 @@ struct _SysprofCallgraphFrame enum { PROP_0, + PROP_CALLGRAPH, PROP_SYMBOL, N_PROPS }; @@ -109,6 +110,10 @@ sysprof_callgraph_frame_get_property (GObject *object, switch (prop_id) { + case PROP_CALLGRAPH: + g_value_set_object (value, self->callgraph); + break; + case PROP_SYMBOL: g_value_set_object (value, sysprof_callgraph_frame_get_symbol (self)); break; @@ -126,6 +131,11 @@ sysprof_callgraph_frame_class_init (SysprofCallgraphFrameClass *klass) object_class->finalize = sysprof_callgraph_frame_finalize; object_class->get_property = sysprof_callgraph_frame_get_property; + properties [PROP_CALLGRAPH] = + g_param_spec_object ("callgraph", NULL, NULL, + SYSPROF_TYPE_CALLGRAPH, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + properties [PROP_SYMBOL] = g_param_spec_object ("symbol", NULL, NULL, SYSPROF_TYPE_SYMBOL, From a7443e05f0163df1745125335f7c189062406c3f Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 9 Jun 2023 15:16:44 -0700 Subject: [PATCH 0382/1030] libsysprof-analyze: add missing header for summary augment --- src/libsysprof-analyze/sysprof-callgraph-frame.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-callgraph-frame.h b/src/libsysprof-analyze/sysprof-callgraph-frame.h index 97b0c6b0..37fbe7a0 100644 --- a/src/libsysprof-analyze/sysprof-callgraph-frame.h +++ b/src/libsysprof-analyze/sysprof-callgraph-frame.h @@ -34,8 +34,10 @@ SYSPROF_AVAILABLE_IN_ALL G_DECLARE_FINAL_TYPE (SysprofCallgraphFrame, sysprof_callgraph_frame, SYSPROF, CALLGRAPH_FRAME, GObject) SYSPROF_AVAILABLE_IN_ALL -SysprofSymbol *sysprof_callgraph_frame_get_symbol (SysprofCallgraphFrame *self); +SysprofSymbol *sysprof_callgraph_frame_get_symbol (SysprofCallgraphFrame *self); SYSPROF_AVAILABLE_IN_ALL -gpointer sysprof_callgraph_frame_get_augment (SysprofCallgraphFrame *self); +gpointer sysprof_callgraph_frame_get_augment (SysprofCallgraphFrame *self); +SYSPROF_AVAILABLE_IN_ALL +gpointer sysprof_callgraph_frame_get_summary_augment (SysprofCallgraphFrame *self); G_END_DECLS From 12f32e8838ab5b321ac525f9df0973efc6395de9 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 9 Jun 2023 15:57:16 -0700 Subject: [PATCH 0383/1030] libsysprof-analyze: add getter for callgraph from frame --- src/libsysprof-analyze/sysprof-callgraph-frame.c | 16 ++++++++++++++++ src/libsysprof-analyze/sysprof-callgraph.h | 2 ++ 2 files changed, 18 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-callgraph-frame.c b/src/libsysprof-analyze/sysprof-callgraph-frame.c index e9ef2a0e..8ba1ba13 100644 --- a/src/libsysprof-analyze/sysprof-callgraph-frame.c +++ b/src/libsysprof-analyze/sysprof-callgraph-frame.c @@ -227,3 +227,19 @@ sysprof_callgraph_frame_get_summary_augment (SysprofCallgraphFrame *self) return sysprof_callgraph_get_summary_augment (self->callgraph, self->node); } + +/** + * sysprof_callgraph_frame_get_callgraph: + * @self: a #SysprofCallgraphFrame + * + * Gets the callgraph the frame belongs to. + * + * Returns: (transfer none) (nullable): a #SysprofCallgraph, or %NULL + */ +SysprofCallgraph * +sysprof_callgraph_frame_get_callgraph (SysprofCallgraphFrame *self) +{ + g_return_val_if_fail (SYSPROF_IS_CALLGRAPH_FRAME (self), NULL); + + return self->callgraph; +} diff --git a/src/libsysprof-analyze/sysprof-callgraph.h b/src/libsysprof-analyze/sysprof-callgraph.h index 5e425712..85c19493 100644 --- a/src/libsysprof-analyze/sysprof-callgraph.h +++ b/src/libsysprof-analyze/sysprof-callgraph.h @@ -70,5 +70,7 @@ gpointer sysprof_callgraph_get_summary_augment (SysprofCallg SysprofCallgraphNode *node); SYSPROF_AVAILABLE_IN_ALL SysprofCallgraphNode *sysprof_callgraph_node_parent (SysprofCallgraphNode *node); +SYSPROF_AVAILABLE_IN_ALL +SysprofCallgraph *sysprof_callgraph_frame_get_callgraph (SysprofCallgraphFrame *self); G_END_DECLS From 2511e79caee81b837d1e697374d0a2faa316b7a5 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 9 Jun 2023 15:57:34 -0700 Subject: [PATCH 0384/1030] libsysprof-analyze: allow getting root augments with NULL --- src/libsysprof-analyze/sysprof-callgraph.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-callgraph.c b/src/libsysprof-analyze/sysprof-callgraph.c index 1d521329..6c13eaf5 100644 --- a/src/libsysprof-analyze/sysprof-callgraph.c +++ b/src/libsysprof-analyze/sysprof-callgraph.c @@ -420,6 +420,9 @@ gpointer sysprof_callgraph_get_augment (SysprofCallgraph *self, SysprofCallgraphNode *node) { + if (node == NULL) + node = &self->root; + return get_augmentation (self, &node->augment); } @@ -427,6 +430,9 @@ gpointer sysprof_callgraph_get_summary_augment (SysprofCallgraph *self, SysprofCallgraphNode *node) { + if (node == NULL) + node = &self->root; + return get_augmentation (self, &node->summary->augment); } From 3f34c4625b73edc3888710a241d888a77364c903 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 9 Jun 2023 15:57:52 -0700 Subject: [PATCH 0385/1030] libsysprof-analyze: cleanup walking to root --- src/libsysprof-analyze/sysprof-callgraph.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-callgraph.c b/src/libsysprof-analyze/sysprof-callgraph.c index 6c13eaf5..4a8b30a0 100644 --- a/src/libsysprof-analyze/sysprof-callgraph.c +++ b/src/libsysprof-analyze/sysprof-callgraph.c @@ -198,16 +198,19 @@ sysprof_callgraph_populate_callers (SysprofCallgraph *self, g_assert (node != NULL); for (const SysprofCallgraphNode *iter = node; - iter != NULL && iter->parent != NULL; + iter != NULL; iter = iter->parent) { - SysprofSymbol *parent_symbol = iter->parent->summary->symbol; - guint pos; - egg_bitset_add (iter->summary->traceables, list_model_index); - if (!g_ptr_array_find (iter->summary->callers, parent_symbol, &pos)) - g_ptr_array_add (iter->summary->callers, parent_symbol); + if (iter->parent != NULL) + { + SysprofSymbol *parent_symbol = iter->parent->summary->symbol; + guint pos; + + if (!g_ptr_array_find (iter->summary->callers, parent_symbol, &pos)) + g_ptr_array_add (iter->summary->callers, parent_symbol); + } } } From d2a38bf1a6de7e50cbddc77c7f9c32909641287c Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 9 Jun 2023 15:58:57 -0700 Subject: [PATCH 0386/1030] libsysprof-analyze: introduce [Unwindable] callgraph node This interjects a node for unwindable traces so that cost gets accounted to the process, but is not just at the [process] graph item as that makes it very hard to subtract to figure out what it was. Instead just insert an [Unwindable] node and cost-account to that. --- src/libsysprof-analyze/sysprof-callgraph.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-callgraph.c b/src/libsysprof-analyze/sysprof-callgraph.c index 4a8b30a0..b6d97921 100644 --- a/src/libsysprof-analyze/sysprof-callgraph.c +++ b/src/libsysprof-analyze/sysprof-callgraph.c @@ -84,6 +84,7 @@ G_DEFINE_FINAL_TYPE_WITH_CODE (SysprofCallgraph, sysprof_callgraph, G_TYPE_OBJEC G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init)) static SysprofSymbol *everything; +static SysprofSymbol *untraceable; static void sysprof_callgraph_summary_free_all (SysprofCallgraphSummary *summary) @@ -182,6 +183,7 @@ sysprof_callgraph_class_init (SysprofCallgraphClass *klass) object_class->finalize = sysprof_callgraph_finalize; everything = _sysprof_symbol_new (g_ref_string_new_intern ("[Everything]"), NULL, NULL, 0, 0); + untraceable = _sysprof_symbol_new (g_ref_string_new_intern ("[Unwindable]"), NULL, NULL, 0, 0); } static void @@ -308,6 +310,18 @@ sysprof_callgraph_add_traceable (SysprofCallgraph *self, g_assert (n_symbols <= stack_depth); + /* Sometimes we get a very unhelpful unwind from the capture + * which is basically a single frame of "user space context". + * That means we got no amount of the stack, but we should + * really account costs to something in the application other + * than the [Application] entry itself so that it's more clear + * that it was a corrupted unwind when recording. + */ + if (n_symbols == 1 && + symbols[0]->is_context_switch && + final_context == SYSPROF_ADDRESS_CONTEXT_USER) + symbols[0] = untraceable; + /* We saved 3 extra spaces for these above so that we can * tack on the "Process" symbol and the "Everything" symbol. * If the final address context places us in Kernel, we want From 739ff7b19c133a27add943da4a8fd8747653f3a7 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 9 Jun 2023 15:59:21 -0700 Subject: [PATCH 0387/1030] libsysprof-gtk: make some column values show up --- .../sysprof-weighted-callgraph-view.c | 52 ++++++++++++++++++- .../sysprof-weighted-callgraph-view.ui | 27 +++++++++- 2 files changed, 76 insertions(+), 3 deletions(-) diff --git a/src/libsysprof-gtk/sysprof-weighted-callgraph-view.c b/src/libsysprof-gtk/sysprof-weighted-callgraph-view.c index 9a4227f6..884a49da 100644 --- a/src/libsysprof-gtk/sysprof-weighted-callgraph-view.c +++ b/src/libsysprof-gtk/sysprof-weighted-callgraph-view.c @@ -61,20 +61,66 @@ augment_weight (SysprofCallgraph *callgraph, cur = sysprof_callgraph_get_augment (callgraph, node); cur->size += 1; + cur->total += 1; sum = sysprof_callgraph_get_summary_augment (callgraph, node); sum->size += 1; + sum->total += 1; - for (; node; node = sysprof_callgraph_node_parent (node)) + for (node = sysprof_callgraph_node_parent (node); + node != NULL; + node = sysprof_callgraph_node_parent (node)) { cur = sysprof_callgraph_get_augment (callgraph, node); cur->total += 1; sum = sysprof_callgraph_get_summary_augment (callgraph, node); - sum->size += 1; + sum->total += 1; } } +static double +get_total_fraction (GObject *item) +{ + g_autoptr(GObject) row = NULL; + + g_object_get (item, "item", &row, NULL); + + if (GTK_IS_TREE_LIST_ROW (row)) + { + GtkTreeListRow *tree_row = GTK_TREE_LIST_ROW (row); + SysprofCallgraphFrame *frame = SYSPROF_CALLGRAPH_FRAME (gtk_tree_list_row_get_item (tree_row)); + SysprofCallgraph *callgraph = sysprof_callgraph_frame_get_callgraph (frame); + AugmentWeight *sum = sysprof_callgraph_frame_get_augment (frame); + AugmentWeight *root = sysprof_callgraph_get_augment (callgraph, NULL); + + return (double)sum->total / (double)root->total; + } + + return 0; +} + +static double +get_self_fraction (GObject *item) +{ + g_autoptr(GObject) row = NULL; + + g_object_get (item, "item", &row, NULL); + + if (GTK_IS_TREE_LIST_ROW (row)) + { + GtkTreeListRow *tree_row = GTK_TREE_LIST_ROW (row); + SysprofCallgraphFrame *frame = SYSPROF_CALLGRAPH_FRAME (gtk_tree_list_row_get_item (tree_row)); + SysprofCallgraph *callgraph = sysprof_callgraph_frame_get_callgraph (frame); + AugmentWeight *sum = sysprof_callgraph_frame_get_augment (frame); + AugmentWeight *root = sysprof_callgraph_get_augment (callgraph, NULL); + + return sum->size / (double)root->total; + } + + return .0; +} + static void sysprof_weighted_callgraph_view_class_init (SysprofWeightedCallgraphViewClass *klass) { @@ -87,6 +133,8 @@ sysprof_weighted_callgraph_view_class_init (SysprofWeightedCallgraphViewClass *k gtk_widget_class_set_template_from_resource (widget_class, "/libsysprof-gtk/sysprof-weighted-callgraph-view.ui"); gtk_widget_class_bind_template_child (widget_class, SysprofWeightedCallgraphView, self_column); gtk_widget_class_bind_template_child (widget_class, SysprofWeightedCallgraphView, total_column); + gtk_widget_class_bind_template_callback (widget_class, get_self_fraction); + gtk_widget_class_bind_template_callback (widget_class, get_total_fraction); g_type_ensure (SYSPROF_TYPE_PROGRESS_CELL); } diff --git a/src/libsysprof-gtk/sysprof-weighted-callgraph-view.ui b/src/libsysprof-gtk/sysprof-weighted-callgraph-view.ui index fc612f14..8609968b 100644 --- a/src/libsysprof-gtk/sysprof-weighted-callgraph-view.ui +++ b/src/libsysprof-gtk/sysprof-weighted-callgraph-view.ui @@ -6,6 +6,27 @@ Self + + + + + + +]]> + + + @@ -19,7 +40,11 @@ From 283da1e107cc90c0aa9d57d15502bc709a24d873 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 9 Jun 2023 16:34:49 -0700 Subject: [PATCH 0388/1030] libsysprof-profile: fix flags type So we can pass it right through. --- src/libsysprof-profile/sysprof-perf-event-stream.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libsysprof-profile/sysprof-perf-event-stream.c b/src/libsysprof-profile/sysprof-perf-event-stream.c index 91e4b45b..4623b2e1 100644 --- a/src/libsysprof-profile/sysprof-perf-event-stream.c +++ b/src/libsysprof-profile/sysprof-perf-event-stream.c @@ -448,7 +448,7 @@ sysprof_perf_event_stream_new (GDBusConnection *connection, struct perf_event_attr *attr, int cpu, int group_fd, - gulong flags, + guint64 flags, SysprofPerfEventCallback callback, gpointer callback_data, GDestroyNotify callback_data_destroy) @@ -509,7 +509,7 @@ sysprof_perf_event_stream_new (GDBusConnection *connection, -1, cpu, group_fd_handle, - (guint64)flags), + flags), G_VARIANT_TYPE ("(h)"), G_DBUS_CALL_FLAGS_NONE, G_MAXUINT, From e8293edd54cca9e13f151b9f95b9bacfd3f09ebc Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 9 Jun 2023 16:35:04 -0700 Subject: [PATCH 0389/1030] libsysprof-profile: only create fdlist when necessary --- src/libsysprof-profile/sysprof-perf-event-stream.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/libsysprof-profile/sysprof-perf-event-stream.c b/src/libsysprof-profile/sysprof-perf-event-stream.c index 4623b2e1..e4b32d5c 100644 --- a/src/libsysprof-profile/sysprof-perf-event-stream.c +++ b/src/libsysprof-profile/sysprof-perf-event-stream.c @@ -492,10 +492,11 @@ sysprof_perf_event_stream_new (GDBusConnection *connection, g_source_set_name (self->source, name); g_source_attach (self->source, NULL); - fd_list = g_unix_fd_list_new (); - if (group_fd > -1) - group_fd_handle = g_unix_fd_list_append (fd_list, group_fd, NULL); + { + fd_list = g_unix_fd_list_new (); + group_fd_handle = g_unix_fd_list_append (fd_list, group_fd, NULL); + } options = build_options_dict (attr); From 9e8c4dba540ac00561e32aaf956d8db3b0b316b6 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 9 Jun 2023 16:35:25 -0700 Subject: [PATCH 0390/1030] libsysprof-profile: init sysprof clock --- src/libsysprof-profile/tests/test-profiler.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libsysprof-profile/tests/test-profiler.c b/src/libsysprof-profile/tests/test-profiler.c index a4554552..09861302 100644 --- a/src/libsysprof-profile/tests/test-profiler.c +++ b/src/libsysprof-profile/tests/test-profiler.c @@ -105,6 +105,8 @@ main (int argc, g_autofd int trace_fd = -1; int argv_copy_len = 0; + sysprof_clock_init (); + main_loop = g_main_loop_new (NULL, FALSE); argv_copy = g_new0 (char *, argc+1); From ee165ea91fbcc9a98f0eac7c761a33d7b8eec411 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 9 Jun 2023 17:23:06 -0700 Subject: [PATCH 0391/1030] libsysprof-profile: add address mappings parser This parses the contents of /proc/$pid/maps and has a crude helper to test it against real world files (which found a missing capture we had). --- src/libsysprof-profile/meson.build | 1 + .../sysprof-maps-parser-private.h | 44 +++++++ src/libsysprof-profile/sysprof-maps-parser.c | 114 ++++++++++++++++++ src/libsysprof-profile/tests/meson.build | 1 + .../tests/test-maps-parser.c | 51 ++++++++ 5 files changed, 211 insertions(+) create mode 100644 src/libsysprof-profile/sysprof-maps-parser-private.h create mode 100644 src/libsysprof-profile/sysprof-maps-parser.c create mode 100644 src/libsysprof-profile/tests/test-maps-parser.c diff --git a/src/libsysprof-profile/meson.build b/src/libsysprof-profile/meson.build index 36a80eb0..cc0ba551 100644 --- a/src/libsysprof-profile/meson.build +++ b/src/libsysprof-profile/meson.build @@ -17,6 +17,7 @@ libsysprof_profile_public_sources = [ libsysprof_profile_private_sources = [ 'mapped-ring-buffer-source.c', 'sysprof-controlfd-instrument.c', + 'sysprof-maps-parser.c', 'sysprof-perf-event-stream.c', 'sysprof-podman.c', 'sysprof-polkit.c', diff --git a/src/libsysprof-profile/sysprof-maps-parser-private.h b/src/libsysprof-profile/sysprof-maps-parser-private.h new file mode 100644 index 00000000..4fbb373e --- /dev/null +++ b/src/libsysprof-profile/sysprof-maps-parser-private.h @@ -0,0 +1,44 @@ +/* sysprof-maps-parser-private.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +#include "line-reader-private.h" + +G_BEGIN_DECLS + +typedef struct _SysprofMapsParser +{ + LineReader reader; +} SysprofMapsParser; + +void sysprof_maps_parser_init (SysprofMapsParser *self, + const char *str, + gssize len); +gboolean sysprof_maps_parser_next (SysprofMapsParser *self, + guint64 *out_begin_addr, + guint64 *out_end_addr, + guint64 *out_offset, + guint64 *out_inode, + char **out_filename); + +G_END_DECLS diff --git a/src/libsysprof-profile/sysprof-maps-parser.c b/src/libsysprof-profile/sysprof-maps-parser.c new file mode 100644 index 00000000..200b1f86 --- /dev/null +++ b/src/libsysprof-profile/sysprof-maps-parser.c @@ -0,0 +1,114 @@ +/* sysprof-maps-parser.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-maps-parser-private.h" + +static GRegex *address_range_regex; + +void +sysprof_maps_parser_init (SysprofMapsParser *self, + const char *str, + gssize len) +{ + line_reader_init (&self->reader, (char *)str, len); + + if (address_range_regex == NULL) + address_range_regex = g_regex_new ("^([0-9a-f]+)-([0-9a-f]+) [r\\-][w\\-][x\\-][ps\\-] ([0-9a-f]+) [0-9]{2}:[0-9]{2} ([0-9]+) +(.*)$", + G_REGEX_OPTIMIZE, + G_REGEX_MATCH_DEFAULT, + NULL); +} + +gboolean +sysprof_maps_parser_next (SysprofMapsParser *self, + guint64 *out_begin_addr, + guint64 *out_end_addr, + guint64 *out_offset, + guint64 *out_inode, + char **out_filename) +{ + const char *line; + gsize len; + + while ((line = line_reader_next (&self->reader, &len))) + { + g_autoptr(GMatchInfo) match_info = NULL; + + if (g_regex_match_full (address_range_regex, line, len, 0, 0, &match_info, NULL)) + { + g_autofree char *file = NULL; + guint64 begin_addr; + guint64 end_addr; + guint64 inode; + guint64 offset; + gboolean is_vdso; + int begin_addr_begin; + int begin_addr_end; + int end_addr_begin; + int end_addr_end; + int offset_begin; + int offset_end; + int inode_begin; + int inode_end; + int path_begin; + int path_end; + + + if (!g_match_info_fetch_pos (match_info, 1, &begin_addr_begin, &begin_addr_end) || + !g_match_info_fetch_pos (match_info, 2, &end_addr_begin, &end_addr_end) || + !g_match_info_fetch_pos (match_info, 3, &offset_begin, &offset_end) || + !g_match_info_fetch_pos (match_info, 4, &inode_begin, &inode_end) || + !g_match_info_fetch_pos (match_info, 5, &path_begin, &path_end)) + continue; + + begin_addr = g_ascii_strtoull (&line[begin_addr_begin], NULL, 16); + end_addr = g_ascii_strtoull (&line[end_addr_begin], NULL, 16); + offset = g_ascii_strtoull (&line[offset_begin], NULL, 16); + inode = g_ascii_strtoull (&line[inode_begin], NULL, 10); + + if (memcmp (" (deleted", + &line[path_end] - strlen (" (deleted"), + strlen (" (deleted")) == 0) + path_end -= strlen (" (deleted)"); + + file = g_strndup (&line[path_begin], path_end-path_begin); + + is_vdso = strcmp ("[vdso]", file) == 0; + + if (is_vdso) + inode = 0; + + if (is_vdso) + offset = 0; + + *out_begin_addr = begin_addr; + *out_end_addr = end_addr; + *out_offset = offset; + *out_inode = inode; + *out_filename = g_steal_pointer (&file); + + return TRUE; + } + } + + return FALSE; +} diff --git a/src/libsysprof-profile/tests/meson.build b/src/libsysprof-profile/tests/meson.build index f559ff7e..f6b5d697 100644 --- a/src/libsysprof-profile/tests/meson.build +++ b/src/libsysprof-profile/tests/meson.build @@ -12,6 +12,7 @@ libsysprof_profile_testsuite_c_args = [ ] libsysprof_profile_testsuite = { + 'test-maps-parser' : {'skip': true}, 'test-profiler' : {'skip': true}, } diff --git a/src/libsysprof-profile/tests/test-maps-parser.c b/src/libsysprof-profile/tests/test-maps-parser.c new file mode 100644 index 00000000..6363bb85 --- /dev/null +++ b/src/libsysprof-profile/tests/test-maps-parser.c @@ -0,0 +1,51 @@ +/* test-maps-parser.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-maps-parser-private.h" + +#define ADDR_FORMAT "0x%"G_GINT64_MODIFIER"x" + +int +main (int argc, + char *argv[]) +{ + g_autoptr(GError) error = NULL; + g_autofree char *contents = NULL; + SysprofMapsParser parser; + guint64 begin, end, offset, inode; + char *file; + gsize len; + + g_assert_cmpint (argc, >, 1); + g_file_get_contents (argv[1], &contents, &len, &error); + g_assert_no_error (error); + + sysprof_maps_parser_init (&parser, contents, len); + while (sysprof_maps_parser_next (&parser, &begin, &end, &offset, &inode, &file)) + { + g_print (ADDR_FORMAT" - "ADDR_FORMAT" "ADDR_FORMAT" %"G_GUINT64_FORMAT" %s\n", + begin, end, offset, inode, file); + g_free (file); + } + + return 0; +} From bfbdd3b64298db4b930b9bb8b53190fb2a7de87e Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 9 Jun 2023 17:23:27 -0700 Subject: [PATCH 0392/1030] libsysprof-profile: use maps parser from Linux instrument --- .../sysprof-linux-instrument.c | 89 ++++--------------- 1 file changed, 18 insertions(+), 71 deletions(-) diff --git a/src/libsysprof-profile/sysprof-linux-instrument.c b/src/libsysprof-profile/sysprof-linux-instrument.c index f43b56b7..4d715064 100644 --- a/src/libsysprof-profile/sysprof-linux-instrument.c +++ b/src/libsysprof-profile/sysprof-linux-instrument.c @@ -23,6 +23,7 @@ #include #include "sysprof-linux-instrument-private.h" +#include "sysprof-maps-parser-private.h" #include "sysprof-podman-private.h" #include "sysprof-recording-private.h" @@ -40,8 +41,6 @@ enum { G_DEFINE_FINAL_TYPE (SysprofLinuxInstrument, sysprof_linux_instrument, SYSPROF_TYPE_INSTRUMENT) -static GRegex *address_range_regex; - static char ** sysprof_linux_instrument_list_required_policy (SysprofInstrument *instrument) { @@ -57,9 +56,9 @@ add_mmaps (SysprofRecording *recording, gboolean ignore_inode) { SysprofCaptureWriter *writer; - LineReader reader; - const char *line; - gsize line_len; + SysprofMapsParser parser; + guint64 begin, end, offset, inode; + char *file; g_assert (SYSPROF_IS_RECORDING (recording)); g_assert (mapsstr != NULL); @@ -67,67 +66,22 @@ add_mmaps (SysprofRecording *recording, writer = _sysprof_recording_writer (recording); - line_reader_init (&reader, (char *)mapsstr, -1); - while ((line = line_reader_next (&reader, &line_len))) + sysprof_maps_parser_init (&parser, mapsstr, -1); + while (sysprof_maps_parser_next (&parser, &begin, &end, &offset, &inode, &file)) { - g_autoptr(GMatchInfo) match_info = NULL; + if (ignore_inode) + inode = 0; - if (g_regex_match_full (address_range_regex, line, line_len, 0, 0, &match_info, NULL)) - { - g_autofree char *file = NULL; - guint64 begin_addr; - guint64 end_addr; - guint64 inode; - guint64 offset; - gboolean is_vdso; - int begin_addr_begin; - int begin_addr_end; - int end_addr_begin; - int end_addr_end; - int offset_begin; - int offset_end; - int inode_begin; - int inode_end; - int path_begin; - int path_end; - - if (!g_match_info_fetch_pos (match_info, 1, &begin_addr_begin, &begin_addr_end) || - !g_match_info_fetch_pos (match_info, 2, &end_addr_begin, &end_addr_end) || - !g_match_info_fetch_pos (match_info, 3, &offset_begin, &offset_end) || - !g_match_info_fetch_pos (match_info, 4, &inode_begin, &inode_end) || - !g_match_info_fetch_pos (match_info, 5, &path_begin, &path_end)) - continue; - - begin_addr = g_ascii_strtoull (&line[begin_addr_begin], NULL, 16); - end_addr = g_ascii_strtoull (&line[end_addr_begin], NULL, 16); - offset = g_ascii_strtoull (&line[offset_begin], NULL, 16); - inode = g_ascii_strtoull (&line[inode_begin], NULL, 10); - - if (memcmp (" (deleted", - &line[path_end] - strlen (" (deleted"), - strlen (" (deleted")) == 0) - path_end -= strlen (" (deleted)"); - - file = g_strndup (&line[path_begin], path_end-path_begin); - - is_vdso = strcmp ("[vdso]", file) == 0; - - if (ignore_inode || is_vdso) - inode = 0; - - if (is_vdso) - offset = 0; - - sysprof_capture_writer_add_map (writer, - SYSPROF_CAPTURE_CURRENT_TIME, - -1, - pid, - begin_addr, - end_addr, - offset, - inode, - file); - } + sysprof_capture_writer_add_map (writer, + SYSPROF_CAPTURE_CURRENT_TIME, + -1, + pid, + begin, + end, + offset, + inode, + file); + g_free (file); } } @@ -402,7 +356,6 @@ sysprof_linux_instrument_class_init (SysprofLinuxInstrumentClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); SysprofInstrumentClass *instrument_class = SYSPROF_INSTRUMENT_CLASS (klass); - g_autoptr(GError) error = NULL; object_class->finalize = sysprof_linux_instrument_finalize; @@ -410,12 +363,6 @@ sysprof_linux_instrument_class_init (SysprofLinuxInstrumentClass *klass) instrument_class->prepare = sysprof_linux_instrument_prepare; instrument_class->record = sysprof_linux_instrument_record; instrument_class->process_started = sysprof_linux_instrument_process_started; - - address_range_regex = g_regex_new ("^([0-9a-f]+)-([0-9a-f]+) [r\\-][w\\-][x\\-][ps\\-] [0-9a-f]+ [0-9]{2}:[0-9]{2} ([0-9]+) +(.*)$", - G_REGEX_OPTIMIZE, - G_REGEX_MATCH_DEFAULT, - &error); - g_assert_no_error (error); } static void From 13e1ace8dc56fa12de2906b6447f5fc63bf6d2fe Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 9 Jun 2023 17:23:43 -0700 Subject: [PATCH 0393/1030] libsysprof-profile: flush events when disabling --- src/libsysprof-profile/sysprof-perf-event-stream.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libsysprof-profile/sysprof-perf-event-stream.c b/src/libsysprof-profile/sysprof-perf-event-stream.c index e4b32d5c..4b5be46e 100644 --- a/src/libsysprof-profile/sysprof-perf-event-stream.c +++ b/src/libsysprof-profile/sysprof-perf-event-stream.c @@ -569,6 +569,8 @@ sysprof_perf_event_stream_disable (SysprofPerfEventStream *self, self->active = FALSE; + sysprof_perf_event_stream_flush (self); + g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ACTIVE]); return TRUE; From 6ed55e48df08cd4c6d2020800c57ba23e022dcb7 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 9 Jun 2023 17:41:22 -0700 Subject: [PATCH 0394/1030] libsysprof-gtk: add load vfunc to handle callgraph --- src/libsysprof-gtk/sysprof-callgraph-view-private.h | 3 +++ src/libsysprof-gtk/sysprof-callgraph-view.c | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/libsysprof-gtk/sysprof-callgraph-view-private.h b/src/libsysprof-gtk/sysprof-callgraph-view-private.h index b33a482d..3521c094 100644 --- a/src/libsysprof-gtk/sysprof-callgraph-view-private.h +++ b/src/libsysprof-gtk/sysprof-callgraph-view-private.h @@ -50,6 +50,9 @@ struct _SysprofCallgraphViewClass SysprofCallgraphNode *node, SysprofDocumentFrame *frame, gpointer user_data); + + void (*load) (SysprofCallgraphView *self, + SysprofCallgraph *callgraph); }; G_END_DECLS diff --git a/src/libsysprof-gtk/sysprof-callgraph-view.c b/src/libsysprof-gtk/sysprof-callgraph-view.c index f046d13c..edb5f731 100644 --- a/src/libsysprof-gtk/sysprof-callgraph-view.c +++ b/src/libsysprof-gtk/sysprof-callgraph-view.c @@ -214,6 +214,9 @@ sysprof_callgraph_view_reload_cb (GObject *object, g_object_ref (GTK_SORTER (sorter))); model = gtk_multi_selection_new (g_object_ref (G_LIST_MODEL (sort_model))); gtk_column_view_set_model (self->column_view, GTK_SELECTION_MODEL (model)); + + if (SYSPROF_CALLGRAPH_VIEW_GET_CLASS (self)->load) + SYSPROF_CALLGRAPH_VIEW_GET_CLASS (self)->load (self, callgraph); } static gboolean From f0d6f2a5f9dc8b5f80bbd3e8656c61018b258f93 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 9 Jun 2023 17:41:41 -0700 Subject: [PATCH 0395/1030] libsysprof-gtk: setup sorters when callgraph is loaded --- .../sysprof-weighted-callgraph-view.c | 68 +++++++++++++++++++ .../sysprof-weighted-callgraph-view.ui | 8 +++ 2 files changed, 76 insertions(+) diff --git a/src/libsysprof-gtk/sysprof-weighted-callgraph-view.c b/src/libsysprof-gtk/sysprof-weighted-callgraph-view.c index 884a49da..35d035ed 100644 --- a/src/libsysprof-gtk/sysprof-weighted-callgraph-view.c +++ b/src/libsysprof-gtk/sysprof-weighted-callgraph-view.c @@ -30,6 +30,9 @@ struct _SysprofWeightedCallgraphView GtkColumnViewColumn *self_column; GtkColumnViewColumn *total_column; + + GtkCustomSorter *self_sorter; + GtkCustomSorter *total_sorter; }; struct _SysprofWeightedCallgraphViewClass @@ -121,6 +124,68 @@ get_self_fraction (GObject *item) return .0; } +static int +sort_by_self (gconstpointer a, + gconstpointer b, + gpointer user_data) +{ + SysprofCallgraphFrame *frame_a = (SysprofCallgraphFrame *)a; + SysprofCallgraphFrame *frame_b = (SysprofCallgraphFrame *)b; + AugmentWeight *aug_a = sysprof_callgraph_frame_get_augment (frame_a); + AugmentWeight *aug_b = sysprof_callgraph_frame_get_augment (frame_b); + AugmentWeight *root = user_data; + double self_a = aug_a->size / (double)root->total; + double self_b = aug_b->size / (double)root->total; + + if (self_a < self_b) + return -1; + else if (self_a > self_b) + return 1; + else + return 0; +} + +static int +sort_by_total (gconstpointer a, + gconstpointer b, + gpointer user_data) +{ + SysprofCallgraphFrame *frame_a = (SysprofCallgraphFrame *)a; + SysprofCallgraphFrame *frame_b = (SysprofCallgraphFrame *)b; + AugmentWeight *aug_a = sysprof_callgraph_frame_get_augment (frame_a); + AugmentWeight *aug_b = sysprof_callgraph_frame_get_augment (frame_b); + AugmentWeight *root = user_data; + double total_a = aug_a->total / (double)root->total; + double total_b = aug_b->total / (double)root->total; + + if (total_a < total_b) + return -1; + else if (total_a > total_b) + return 1; + else + return 0; +} + +static void +sysprof_weighted_callgraph_view_load (SysprofCallgraphView *view, + SysprofCallgraph *callgraph) +{ + SysprofWeightedCallgraphView *self = (SysprofWeightedCallgraphView *)view; + AugmentWeight *root; + + g_assert (SYSPROF_IS_WEIGHTED_CALLGRAPH_VIEW (self)); + g_assert (SYSPROF_IS_CALLGRAPH (callgraph)); + + root = sysprof_callgraph_get_augment (callgraph, NULL); + + gtk_custom_sorter_set_sort_func (self->self_sorter, sort_by_self, root, NULL); + gtk_custom_sorter_set_sort_func (self->total_sorter, sort_by_total, root, NULL); + + gtk_column_view_sort_by_column (SYSPROF_CALLGRAPH_VIEW (self)->column_view, + self->total_column, + GTK_SORT_DESCENDING); +} + static void sysprof_weighted_callgraph_view_class_init (SysprofWeightedCallgraphViewClass *klass) { @@ -129,10 +194,13 @@ sysprof_weighted_callgraph_view_class_init (SysprofWeightedCallgraphViewClass *k callgraph_view_class->augment_size = sizeof (AugmentWeight); callgraph_view_class->augment_func = augment_weight; + callgraph_view_class->load = sysprof_weighted_callgraph_view_load; gtk_widget_class_set_template_from_resource (widget_class, "/libsysprof-gtk/sysprof-weighted-callgraph-view.ui"); gtk_widget_class_bind_template_child (widget_class, SysprofWeightedCallgraphView, self_column); gtk_widget_class_bind_template_child (widget_class, SysprofWeightedCallgraphView, total_column); + gtk_widget_class_bind_template_child (widget_class, SysprofWeightedCallgraphView, self_sorter); + gtk_widget_class_bind_template_child (widget_class, SysprofWeightedCallgraphView, total_sorter); gtk_widget_class_bind_template_callback (widget_class, get_self_fraction); gtk_widget_class_bind_template_callback (widget_class, get_total_fraction); diff --git a/src/libsysprof-gtk/sysprof-weighted-callgraph-view.ui b/src/libsysprof-gtk/sysprof-weighted-callgraph-view.ui index 8609968b..56260171 100644 --- a/src/libsysprof-gtk/sysprof-weighted-callgraph-view.ui +++ b/src/libsysprof-gtk/sysprof-weighted-callgraph-view.ui @@ -27,6 +27,10 @@ + + + + @@ -53,6 +57,10 @@ + + + + From b2ded6d231fd321893532e163fde90e1007b7ed5 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 9 Jun 2023 17:49:29 -0700 Subject: [PATCH 0396/1030] libsysprof-gtk: use inscription for function name --- src/libsysprof-gtk/sysprof-callgraph-view.ui | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/libsysprof-gtk/sysprof-callgraph-view.ui b/src/libsysprof-gtk/sysprof-callgraph-view.ui index 54ea7d97..f85a9d9e 100644 --- a/src/libsysprof-gtk/sysprof-callgraph-view.ui +++ b/src/libsysprof-gtk/sysprof-callgraph-view.ui @@ -41,12 +41,10 @@ - - start + + 0 true - true - end - + expander @@ -61,8 +59,6 @@ false true none - 6 - 6 From 6b242acbc24b8f667fb7d8d2cb1f52832a6677c5 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 9 Jun 2023 17:53:05 -0700 Subject: [PATCH 0397/1030] libsysprof-gtk: implement a11y bits for cell progress --- src/libsysprof-gtk/sysprof-progress-cell.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/libsysprof-gtk/sysprof-progress-cell.c b/src/libsysprof-gtk/sysprof-progress-cell.c index 7797211c..77748502 100644 --- a/src/libsysprof-gtk/sysprof-progress-cell.c +++ b/src/libsysprof-gtk/sysprof-progress-cell.c @@ -204,6 +204,7 @@ sysprof_progress_cell_class_init (SysprofProgressCellClass *klass) g_object_class_install_properties (object_class, N_PROPS, properties); gtk_widget_class_set_css_name (widget_class, "progresscell"); + gtk_widget_class_set_accessible_role (widget_class, GTK_ACCESSIBLE_ROLE_PROGRESS_BAR); } static void @@ -238,6 +239,12 @@ sysprof_progress_cell_init (SysprofProgressCell *self) gtk_widget_set_parent (GTK_WIDGET (self->label), GTK_WIDGET (self)); gtk_widget_set_parent (GTK_WIDGET (self->progress), GTK_WIDGET (self)); gtk_widget_set_parent (GTK_WIDGET (self->alt_label), GTK_WIDGET (self)); + + gtk_accessible_update_property (GTK_ACCESSIBLE (self), + GTK_ACCESSIBLE_PROPERTY_VALUE_MAX, 1.0, + GTK_ACCESSIBLE_PROPERTY_VALUE_MIN, 0.0, + GTK_ACCESSIBLE_PROPERTY_VALUE_NOW, 0.0, + -1); } GtkWidget * @@ -273,8 +280,15 @@ sysprof_progress_cell_set_fraction (SysprofProgressCell *self, gtk_label_set_text (self->alt_label, text); gtk_widget_set_visible (GTK_WIDGET (self->progress), fraction > .0); - gtk_widget_queue_allocate (GTK_WIDGET (self)); + + gtk_accessible_update_property (GTK_ACCESSIBLE (self), + GTK_ACCESSIBLE_PROPERTY_VALUE_MAX, 1.0, + GTK_ACCESSIBLE_PROPERTY_VALUE_MIN, 0.0, + GTK_ACCESSIBLE_PROPERTY_VALUE_NOW, fraction, + GTK_ACCESSIBLE_PROPERTY_VALUE_TEXT, text, + -1); g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FRACTION]); + gtk_widget_queue_allocate (GTK_WIDGET (self)); } } From 595045c3fcd07bd4a8b8bd97d54e88638f936184 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 9 Jun 2023 18:00:43 -0700 Subject: [PATCH 0398/1030] libsysprof-gtk: expand to process list by default --- src/libsysprof-gtk/sysprof-callgraph-view.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libsysprof-gtk/sysprof-callgraph-view.c b/src/libsysprof-gtk/sysprof-callgraph-view.c index edb5f731..83974b5c 100644 --- a/src/libsysprof-gtk/sysprof-callgraph-view.c +++ b/src/libsysprof-gtk/sysprof-callgraph-view.c @@ -189,6 +189,7 @@ sysprof_callgraph_view_reload_cb (GObject *object, g_autoptr(GtkMultiSelection) model = NULL; g_autoptr(GtkSortListModel) sort_model = NULL; g_autoptr(GtkTreeListModel) tree = NULL; + g_autoptr(GtkTreeListRow) first = NULL; g_autoptr(GError) error = NULL; GtkSorter *column_sorter; @@ -217,6 +218,9 @@ sysprof_callgraph_view_reload_cb (GObject *object, if (SYSPROF_CALLGRAPH_VIEW_GET_CLASS (self)->load) SYSPROF_CALLGRAPH_VIEW_GET_CLASS (self)->load (self, callgraph); + + if ((first = gtk_tree_list_model_get_row (tree, 0))) + gtk_tree_list_row_set_expanded (first, TRUE); } static gboolean From d8adcb5b4809ef0441284c436058b1d3711884bf Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 9 Jun 2023 21:21:09 -0700 Subject: [PATCH 0399/1030] libsysprof-gtk: reduce chances of cell causing column width change --- src/libsysprof-gtk/sysprof-progress-cell.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libsysprof-gtk/sysprof-progress-cell.c b/src/libsysprof-gtk/sysprof-progress-cell.c index 77748502..51b399b3 100644 --- a/src/libsysprof-gtk/sysprof-progress-cell.c +++ b/src/libsysprof-gtk/sysprof-progress-cell.c @@ -217,14 +217,16 @@ sysprof_progress_cell_init (SysprofProgressCell *self) g_snprintf (percent, sizeof percent, "%6.2lf%%", .0); self->label = g_object_new (GTK_TYPE_LABEL, - "halign", GTK_ALIGN_END, + "xalign", 1.f, "valign", GTK_ALIGN_CENTER, "label", percent, + "width-chars", 7, NULL); self->alt_label = g_object_new (GTK_TYPE_LABEL, - "halign", GTK_ALIGN_END, + "xalign", 1.f, "valign", GTK_ALIGN_CENTER, "label", percent, + "width-chars", 7, NULL); gtk_widget_add_css_class (GTK_WIDGET (self->alt_label), "in-progress"); self->trough = g_object_new (ADW_TYPE_BIN, From 643aa374cbe36a1c656d513b0742d27410c83d5d Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 12 Jun 2023 11:15:50 -0700 Subject: [PATCH 0400/1030] libsysprof-analyze: add GListModel of callgraph symbols This has an indirect object (SysprofCallgraphSymbol) so that we can provide plumbing to get the callgraph as well as the augmented data from the indirection object. This allows for callgraphs to show overview in the functions list which contains the same augmentation that the descendants view does. --- src/libsysprof-analyze/meson.build | 2 + src/libsysprof-analyze/sysprof-analyze.h | 1 + .../sysprof-callgraph-private.h | 42 ++- .../sysprof-callgraph-symbol-private.h | 29 +++ .../sysprof-callgraph-symbol.c | 241 ++++++++++++++++++ .../sysprof-callgraph-symbol.h | 41 +++ src/libsysprof-analyze/sysprof-callgraph.c | 52 ++-- src/libsysprof-analyze/sysprof-callgraph.h | 25 +- 8 files changed, 395 insertions(+), 38 deletions(-) create mode 100644 src/libsysprof-analyze/sysprof-callgraph-symbol-private.h create mode 100644 src/libsysprof-analyze/sysprof-callgraph-symbol.c create mode 100644 src/libsysprof-analyze/sysprof-callgraph-symbol.h diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build index 13659d9a..ae7bd11f 100644 --- a/src/libsysprof-analyze/meson.build +++ b/src/libsysprof-analyze/meson.build @@ -2,6 +2,7 @@ libsysprof_analyze_public_sources = [ 'sysprof-bundled-symbolizer.c', 'sysprof-callgraph.c', 'sysprof-callgraph-frame.c', + 'sysprof-callgraph-symbol.c', 'sysprof-document.c', 'sysprof-document-allocation.c', 'sysprof-document-counter.c', @@ -50,6 +51,7 @@ libsysprof_analyze_public_headers = [ 'sysprof-analyze.h', 'sysprof-callgraph.h', 'sysprof-callgraph-frame.h', + 'sysprof-callgraph-symbol.h', 'sysprof-bundled-symbolizer.h', 'sysprof-document.h', 'sysprof-document-allocation.h', diff --git a/src/libsysprof-analyze/sysprof-analyze.h b/src/libsysprof-analyze/sysprof-analyze.h index 4eda88a9..7e9d2518 100644 --- a/src/libsysprof-analyze/sysprof-analyze.h +++ b/src/libsysprof-analyze/sysprof-analyze.h @@ -28,6 +28,7 @@ G_BEGIN_DECLS # include "sysprof-bundled-symbolizer.h" # include "sysprof-callgraph.h" # include "sysprof-callgraph-frame.h" +# include "sysprof-callgraph-symbol.h" # include "sysprof-document.h" # include "sysprof-document-allocation.h" # include "sysprof-document-counter.h" diff --git a/src/libsysprof-analyze/sysprof-callgraph-private.h b/src/libsysprof-analyze/sysprof-callgraph-private.h index c2eb7c52..53a6da34 100644 --- a/src/libsysprof-analyze/sysprof-callgraph-private.h +++ b/src/libsysprof-analyze/sysprof-callgraph-private.h @@ -47,16 +47,36 @@ struct _SysprofCallgraphNode gpointer augment; }; -void _sysprof_callgraph_new_async (SysprofDocument *document, - GListModel *traceables, - gsize augment_size, - SysprofAugmentationFunc augment_func, - gpointer augment_func_data, - GDestroyNotify augment_func_data_destroy, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -SysprofCallgraph *_sysprof_callgraph_new_finish (GAsyncResult *result, - GError **error); +struct _SysprofCallgraph +{ + GObject parent_instance; + + SysprofDocument *document; + GListModel *traceables; + + GHashTable *symbol_to_summary; + GPtrArray *symbols; + + gsize augment_size; + SysprofAugmentationFunc augment_func; + gpointer augment_func_data; + GDestroyNotify augment_func_data_destroy; + + SysprofCallgraphNode root; +}; + +void _sysprof_callgraph_new_async (SysprofDocument *document, + GListModel *traceables, + gsize augment_size, + SysprofAugmentationFunc augment_func, + gpointer augment_func_data, + GDestroyNotify augment_func_data_destroy, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +SysprofCallgraph *_sysprof_callgraph_new_finish (GAsyncResult *result, + GError **error); +gpointer _sysprof_callgraph_get_symbol_augment (SysprofCallgraph *self, + SysprofSymbol *symbol); G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-callgraph-symbol-private.h b/src/libsysprof-analyze/sysprof-callgraph-symbol-private.h new file mode 100644 index 00000000..1c3adc97 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-callgraph-symbol-private.h @@ -0,0 +1,29 @@ +/* sysprof-callgraph-symbol-private.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-callgraph.h" + +G_BEGIN_DECLS + +GListModel *_sysprof_callgraph_symbol_list_model_new (SysprofCallgraph *callgraph); + +G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-callgraph-symbol.c b/src/libsysprof-analyze/sysprof-callgraph-symbol.c new file mode 100644 index 00000000..9fe8a31a --- /dev/null +++ b/src/libsysprof-analyze/sysprof-callgraph-symbol.c @@ -0,0 +1,241 @@ +/* sysprof-callgraph-symbol.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include + +#include "sysprof-callgraph-private.h" +#include "sysprof-callgraph-symbol-private.h" + +struct _SysprofCallgraphSymbol +{ + GObject parent_instance; + SysprofCallgraph *callgraph; + SysprofSymbol *symbol; +}; + +enum { + PROP_0, + PROP_CALLGRAPH, + PROP_SYMBOL, + N_PROPS +}; + +G_DEFINE_FINAL_TYPE (SysprofCallgraphSymbol, sysprof_callgraph_symbol, G_TYPE_OBJECT) + +static GParamSpec *properties [N_PROPS]; + +static void +sysprof_callgraph_symbol_finalize (GObject *object) +{ + SysprofCallgraphSymbol *self = (SysprofCallgraphSymbol *)object; + + g_clear_weak_pointer (&self->callgraph); + g_clear_object (&self->symbol); + + G_OBJECT_CLASS (sysprof_callgraph_symbol_parent_class)->finalize (object); +} + +static void +sysprof_callgraph_symbol_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofCallgraphSymbol *self = SYSPROF_CALLGRAPH_SYMBOL (object); + + switch (prop_id) + { + case PROP_CALLGRAPH: + g_value_set_object (value, self->callgraph); + break; + + case PROP_SYMBOL: + g_value_set_object (value, sysprof_callgraph_symbol_get_symbol (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_callgraph_symbol_class_init (SysprofCallgraphSymbolClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = sysprof_callgraph_symbol_finalize; + object_class->get_property = sysprof_callgraph_symbol_get_property; + + properties [PROP_CALLGRAPH] = + g_param_spec_object ("callgraph", NULL, NULL, + SYSPROF_TYPE_CALLGRAPH, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + properties [PROP_SYMBOL] = + g_param_spec_object ("symbol", NULL, NULL, + SYSPROF_TYPE_SYMBOL, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); +} + +static void +sysprof_callgraph_symbol_init (SysprofCallgraphSymbol *self) +{ +} + +SysprofCallgraphSymbol * +_sysprof_callgraph_symbol_new (SysprofCallgraph *callgraph, + SysprofSymbol *symbol) +{ + SysprofCallgraphSymbol *self; + + g_return_val_if_fail (SYSPROF_IS_CALLGRAPH (callgraph), NULL); + g_return_val_if_fail (SYSPROF_IS_SYMBOL (symbol), NULL); + + self = g_object_new (SYSPROF_TYPE_CALLGRAPH_SYMBOL, NULL); + g_set_weak_pointer (&self->callgraph, callgraph); + g_set_object (&self->symbol, symbol); + + return self; +} + +/** + * sysprof_callgraph_symbol_get_symbol: + * @self: a #SysprofCallgraphSymbol + * + * Gets the symbol for the symbol. + * + * Returns: (nullable) (transfer none): a #SysprofSymbol + */ +SysprofSymbol * +sysprof_callgraph_symbol_get_symbol (SysprofCallgraphSymbol *self) +{ + g_return_val_if_fail (SYSPROF_IS_CALLGRAPH_SYMBOL (self), NULL); + + return self->symbol; +} + +/** + * sysprof_callgraph_symbol_get_summary_augment: (skip) + * @self: a #SysprofCallgraphSymbol + * + * Gets the augmentation that was attached to the summary for + * the callgraph node's symbol. + * + * Returns: (nullable) (transfer none): the augmentation data + */ +gpointer +sysprof_callgraph_symbol_get_summary_augment (SysprofCallgraphSymbol *self) +{ + g_return_val_if_fail (SYSPROF_IS_CALLGRAPH_SYMBOL (self), NULL); + + if (self->callgraph == NULL) + return NULL; + + return _sysprof_callgraph_get_symbol_augment (self->callgraph, self->symbol); +} + +/** + * sysprof_callgraph_symbol_get_callgraph: + * @self: a #SysprofCallgraphSymbol + * + * Gets the callgraph the symbol belongs to. + * + * Returns: (transfer none) (nullable): a #SysprofCallgraph, or %NULL + */ +SysprofCallgraph * +sysprof_callgraph_symbol_get_callgraph (SysprofCallgraphSymbol *self) +{ + g_return_val_if_fail (SYSPROF_IS_CALLGRAPH_SYMBOL (self), NULL); + + return self->callgraph; +} + +struct _SysprofCallgraphSymbolListModel +{ + GObject parent_instance; + SysprofCallgraph *callgraph; +}; + +static guint +sysprof_callgraph_symbol_list_model_get_n_items (GListModel *model) +{ + return SYSPROF_CALLGRAPH_SYMBOL (model)->callgraph->symbols->len; +} + +static GType +sysprof_callgraph_symbol_list_model_get_item_type (GListModel *model) +{ + return SYSPROF_TYPE_CALLGRAPH_SYMBOL; +} + +static gpointer +sysprof_callgraph_symbol_list_model_get_item (GListModel *model, + guint position) +{ + SysprofCallgraphSymbol *self = SYSPROF_CALLGRAPH_SYMBOL (model); + + if (position >= self->callgraph->symbols->len) + return NULL; + + return _sysprof_callgraph_symbol_new (self->callgraph, + g_ptr_array_index (self->callgraph->symbols, position)); +} + +static void +list_model_iface_init (GListModelInterface *iface) +{ + iface->get_n_items = sysprof_callgraph_symbol_list_model_get_n_items; + iface->get_item_type = sysprof_callgraph_symbol_list_model_get_item_type; + iface->get_item = sysprof_callgraph_symbol_list_model_get_item; +} + +#define SYSPROF_TYPE_CALLGRAPH_SYMBOL_LIST_MODEL (sysprof_callgraph_symbol_list_model_get_type()) +G_DECLARE_FINAL_TYPE (SysprofCallgraphSymbolListModel, sysprof_callgraph_symbol_list_model, SYSPROF, CALLGRAPH_SYMBOL_LIST_MODEL, GObject) +G_DEFINE_FINAL_TYPE_WITH_CODE (SysprofCallgraphSymbolListModel, sysprof_callgraph_symbol_list_model, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init)) + +static void +sysprof_callgraph_symbol_list_model_class_init (SysprofCallgraphSymbolListModelClass *klass) +{ +} + +static void +sysprof_callgraph_symbol_list_model_init (SysprofCallgraphSymbolListModel *self) +{ +} + +GListModel * +_sysprof_callgraph_symbol_list_model_new (SysprofCallgraph *callgraph) +{ + SysprofCallgraphSymbolListModel *self; + + g_return_val_if_fail (SYSPROF_IS_CALLGRAPH (callgraph), NULL); + + self = g_object_new (SYSPROF_TYPE_CALLGRAPH_SYMBOL_LIST_MODEL, NULL); + self->callgraph = g_object_ref (callgraph); + + return G_LIST_MODEL (self); +} + +G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-callgraph-symbol.h b/src/libsysprof-analyze/sysprof-callgraph-symbol.h new file mode 100644 index 00000000..a6360fbd --- /dev/null +++ b/src/libsysprof-analyze/sysprof-callgraph-symbol.h @@ -0,0 +1,41 @@ +/* sysprof-callgraph-symbol.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +#include + +#include "sysprof-symbol.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_CALLGRAPH_SYMBOL (sysprof_callgraph_symbol_get_type()) + +SYSPROF_AVAILABLE_IN_ALL +G_DECLARE_FINAL_TYPE (SysprofCallgraphSymbol, sysprof_callgraph_symbol, SYSPROF, CALLGRAPH_SYMBOL, GObject) + +SYSPROF_AVAILABLE_IN_ALL +SysprofSymbol *sysprof_callgraph_symbol_get_symbol (SysprofCallgraphSymbol *self); +SYSPROF_AVAILABLE_IN_ALL +gpointer sysprof_callgraph_symbol_get_summary_augment (SysprofCallgraphSymbol *self); + +G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-callgraph.c b/src/libsysprof-analyze/sysprof-callgraph.c index b6d97921..464c893b 100644 --- a/src/libsysprof-analyze/sysprof-callgraph.c +++ b/src/libsysprof-analyze/sysprof-callgraph.c @@ -22,6 +22,7 @@ #include "sysprof-callgraph-private.h" #include "sysprof-callgraph-frame-private.h" +#include "sysprof-callgraph-symbol-private.h" #include "sysprof-document-bitset-index-private.h" #include "sysprof-document-private.h" #include "sysprof-document-traceable.h" @@ -31,23 +32,6 @@ #define MAX_STACK_DEPTH 1024 -struct _SysprofCallgraph -{ - GObject parent_instance; - - SysprofDocument *document; - GListModel *traceables; - - GHashTable *symbol_to_summary; - - gsize augment_size; - SysprofAugmentationFunc augment_func; - gpointer augment_func_data; - GDestroyNotify augment_func_data_destroy; - - SysprofCallgraphNode root; -}; - static GType sysprof_callgraph_get_item_type (GListModel *model) { @@ -119,6 +103,7 @@ sysprof_callgraph_get_summary (SysprofCallgraph *self, summary->symbol = symbol; g_hash_table_insert (self->symbol_to_summary, symbol, summary); + g_ptr_array_add (self->symbols, symbol); } return summary; @@ -400,6 +385,7 @@ _sysprof_callgraph_new_async (SysprofDocument *document, self->augment_func_data = augment_func_data; self->augment_func_data_destroy = augment_func_data_destroy; self->symbol_to_summary = g_hash_table_new_full (NULL, NULL, NULL, summary_free); + self->symbols = g_ptr_array_new (); self->root.summary = sysprof_callgraph_get_summary (self, everything); task = g_task_new (NULL, cancellable, callback, user_data); @@ -453,6 +439,21 @@ sysprof_callgraph_get_summary_augment (SysprofCallgraph *self, return get_augmentation (self, &node->summary->augment); } +gpointer +_sysprof_callgraph_get_symbol_augment (SysprofCallgraph *self, + SysprofSymbol *symbol) +{ + SysprofCallgraphSummary *summary; + + g_return_val_if_fail (SYSPROF_IS_CALLGRAPH (self), NULL); + g_return_val_if_fail (SYSPROF_IS_SYMBOL (symbol), NULL); + + if ((summary = g_hash_table_lookup (self->symbol_to_summary, symbol))) + return get_augmentation (self, &summary->augment); + + return NULL; +} + SysprofCallgraphNode * sysprof_callgraph_node_parent (SysprofCallgraphNode *node) { @@ -512,3 +513,20 @@ sysprof_callgraph_list_traceables_for_symbol (SysprofCallgraph *self, return G_LIST_MODEL (g_list_store_new (SYSPROF_TYPE_DOCUMENT_TRACEABLE)); } + +/** + * sysprof_callgraph_list_symbols: + * @self: a #SysprofCallgraph + * + * Gets a #GListModel of #SysprofCallgraphSymbol that an be used to + * display a function list and associated augmentation data. + * + * Returns: (transfer full): a #GListModel of #SysprofCallgraphSymbol + */ +GListModel * +sysprof_callgraph_list_symbols (SysprofCallgraph *self) +{ + g_return_val_if_fail (SYSPROF_IS_CALLGRAPH (self), NULL); + + return _sysprof_callgraph_symbol_list_model_new (self); +} diff --git a/src/libsysprof-analyze/sysprof-callgraph.h b/src/libsysprof-analyze/sysprof-callgraph.h index 85c19493..3dfbad4b 100644 --- a/src/libsysprof-analyze/sysprof-callgraph.h +++ b/src/libsysprof-analyze/sysprof-callgraph.h @@ -25,6 +25,7 @@ #include #include "sysprof-callgraph-frame.h" +#include "sysprof-callgraph-symbol.h" #include "sysprof-document-frame.h" G_BEGIN_DECLS @@ -57,20 +58,24 @@ typedef void (*SysprofAugmentationFunc) (SysprofCallgraph *callgraph, gpointer user_data); SYSPROF_AVAILABLE_IN_ALL -GListModel *sysprof_callgraph_list_callers (SysprofCallgraph *self, - SysprofCallgraphFrame *frame); +GListModel *sysprof_callgraph_list_symbols (SysprofCallgraph *self); SYSPROF_AVAILABLE_IN_ALL -GListModel *sysprof_callgraph_list_traceables_for_symbol (SysprofCallgraph *self, - SysprofSymbol *symbol); +GListModel *sysprof_callgraph_list_callers (SysprofCallgraph *self, + SysprofCallgraphFrame *frame); SYSPROF_AVAILABLE_IN_ALL -gpointer sysprof_callgraph_get_augment (SysprofCallgraph *self, - SysprofCallgraphNode *node); +GListModel *sysprof_callgraph_list_traceables_for_symbol (SysprofCallgraph *self, + SysprofSymbol *symbol); SYSPROF_AVAILABLE_IN_ALL -gpointer sysprof_callgraph_get_summary_augment (SysprofCallgraph *self, - SysprofCallgraphNode *node); +gpointer sysprof_callgraph_get_augment (SysprofCallgraph *self, + SysprofCallgraphNode *node); SYSPROF_AVAILABLE_IN_ALL -SysprofCallgraphNode *sysprof_callgraph_node_parent (SysprofCallgraphNode *node); +gpointer sysprof_callgraph_get_summary_augment (SysprofCallgraph *self, + SysprofCallgraphNode *node); SYSPROF_AVAILABLE_IN_ALL -SysprofCallgraph *sysprof_callgraph_frame_get_callgraph (SysprofCallgraphFrame *self); +SysprofCallgraphNode *sysprof_callgraph_node_parent (SysprofCallgraphNode *node); +SYSPROF_AVAILABLE_IN_ALL +SysprofCallgraph *sysprof_callgraph_frame_get_callgraph (SysprofCallgraphFrame *self); +SYSPROF_AVAILABLE_IN_ALL +SysprofCallgraph *sysprof_callgraph_symbol_get_callgraph (SysprofCallgraphSymbol *self); G_END_DECLS From 451e79a84cb899a024d494ba8bc4477e690abc80 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 12 Jun 2023 11:16:41 -0700 Subject: [PATCH 0401/1030] libsysprof-gtk: add augmented functions list This provides an area to have augmented functions listing like we have in the 3-part sysprof view in our current release. The weighed callgrpah view uses this to show self/total sortable columns similar to the descendants view. --- .../sysprof-callgraph-view-private.h | 4 +- src/libsysprof-gtk/sysprof-callgraph-view.c | 17 ++- src/libsysprof-gtk/sysprof-callgraph-view.ui | 78 +++++++++--- .../sysprof-weighted-callgraph-view.c | 119 ++++++++++++++++-- .../sysprof-weighted-callgraph-view.ui | 67 ++++++++++ 5 files changed, 259 insertions(+), 26 deletions(-) diff --git a/src/libsysprof-gtk/sysprof-callgraph-view-private.h b/src/libsysprof-gtk/sysprof-callgraph-view-private.h index 3521c094..031cb32d 100644 --- a/src/libsysprof-gtk/sysprof-callgraph-view-private.h +++ b/src/libsysprof-gtk/sysprof-callgraph-view-private.h @@ -34,7 +34,9 @@ struct _SysprofCallgraphView GListModel *traceables; GtkColumnView *column_view; - GtkWidget *scrolled_window; + GtkColumnView *functions_column_view; + GtkScrolledWindow *scrolled_window; + GtkWidget *paned; GCancellable *cancellable; diff --git a/src/libsysprof-gtk/sysprof-callgraph-view.c b/src/libsysprof-gtk/sysprof-callgraph-view.c index 83974b5c..fd31dfd8 100644 --- a/src/libsysprof-gtk/sysprof-callgraph-view.c +++ b/src/libsysprof-gtk/sysprof-callgraph-view.c @@ -71,7 +71,7 @@ sysprof_callgraph_view_dispose (GObject *object) g_clear_handle_id (&self->reload_source, g_source_remove); - g_clear_pointer ((GtkWidget **)&self->scrolled_window, gtk_widget_unparent); + g_clear_pointer (&self->paned, gtk_widget_unparent); g_cancellable_cancel (self->cancellable); g_clear_object (&self->cancellable); @@ -153,6 +153,8 @@ sysprof_callgraph_view_class_init (SysprofCallgraphViewClass *klass) gtk_widget_class_set_template_from_resource (widget_class, "/libsysprof-gtk/sysprof-callgraph-view.ui"); gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT); gtk_widget_class_bind_template_child (widget_class, SysprofCallgraphView, column_view); + gtk_widget_class_bind_template_child (widget_class, SysprofCallgraphView, functions_column_view); + gtk_widget_class_bind_template_child (widget_class, SysprofCallgraphView, paned); gtk_widget_class_bind_template_child (widget_class, SysprofCallgraphView, scrolled_window); gtk_widget_class_bind_template_callback (widget_class, sysprof_callgraph_view_key_pressed_cb); @@ -187,7 +189,10 @@ sysprof_callgraph_view_reload_cb (GObject *object, g_autoptr(SysprofCallgraph) callgraph = NULL; g_autoptr(GtkTreeListRowSorter) sorter = NULL; g_autoptr(GtkMultiSelection) model = NULL; + g_autoptr(GtkSingleSelection) functions_selection = NULL; g_autoptr(GtkSortListModel) sort_model = NULL; + g_autoptr(GtkSortListModel) functions_sort_model = NULL; + g_autoptr(GListModel) functions_model = NULL; g_autoptr(GtkTreeListModel) tree = NULL; g_autoptr(GtkTreeListRow) first = NULL; g_autoptr(GError) error = NULL; @@ -216,6 +221,14 @@ sysprof_callgraph_view_reload_cb (GObject *object, model = gtk_multi_selection_new (g_object_ref (G_LIST_MODEL (sort_model))); gtk_column_view_set_model (self->column_view, GTK_SELECTION_MODEL (model)); + column_sorter = gtk_column_view_get_sorter (self->functions_column_view); + functions_model = sysprof_callgraph_list_symbols (callgraph); + functions_sort_model = gtk_sort_list_model_new (g_object_ref (functions_model), + g_object_ref (column_sorter)); + functions_selection = gtk_single_selection_new (g_object_ref (G_LIST_MODEL (functions_sort_model))); + gtk_column_view_set_model (self->functions_column_view, + GTK_SELECTION_MODEL (functions_selection)); + if (SYSPROF_CALLGRAPH_VIEW_GET_CLASS (self)->load) SYSPROF_CALLGRAPH_VIEW_GET_CLASS (self)->load (self, callgraph); @@ -329,6 +342,8 @@ sysprof_callgraph_view_get_internal_child (GtkBuildable *buildable, { if (g_strcmp0 (name, "column_view") == 0) return G_OBJECT (SYSPROF_CALLGRAPH_VIEW (buildable)->column_view); + else if (g_strcmp0 (name, "functions_column_view") == 0) + return G_OBJECT (SYSPROF_CALLGRAPH_VIEW (buildable)->functions_column_view); return NULL; } diff --git a/src/libsysprof-gtk/sysprof-callgraph-view.ui b/src/libsysprof-gtk/sysprof-callgraph-view.ui index f85a9d9e..6b7e0469 100644 --- a/src/libsysprof-gtk/sysprof-callgraph-view.ui +++ b/src/libsysprof-gtk/sysprof-callgraph-view.ui @@ -2,19 +2,67 @@ diff --git a/src/libsysprof-gtk/sysprof-weighted-callgraph-view.c b/src/libsysprof-gtk/sysprof-weighted-callgraph-view.c index 35d035ed..74cc5e26 100644 --- a/src/libsysprof-gtk/sysprof-weighted-callgraph-view.c +++ b/src/libsysprof-gtk/sysprof-weighted-callgraph-view.c @@ -30,9 +30,13 @@ struct _SysprofWeightedCallgraphView GtkColumnViewColumn *self_column; GtkColumnViewColumn *total_column; - GtkCustomSorter *self_sorter; GtkCustomSorter *total_sorter; + + GtkColumnViewColumn *functions_self_column; + GtkColumnViewColumn *functions_total_column; + GtkCustomSorter *functions_self_sorter; + GtkCustomSorter *functions_total_sorter; }; struct _SysprofWeightedCallgraphViewClass @@ -97,7 +101,7 @@ get_total_fraction (GObject *item) AugmentWeight *sum = sysprof_callgraph_frame_get_augment (frame); AugmentWeight *root = sysprof_callgraph_get_augment (callgraph, NULL); - return (double)sum->total / (double)root->total; + return sum->total / (double)root->total; } return 0; @@ -124,10 +128,48 @@ get_self_fraction (GObject *item) return .0; } +static double +functions_get_total_fraction (GObject *item) +{ + g_autoptr(SysprofCallgraphSymbol) sym = NULL; + + g_object_get (item, "item", &sym, NULL); + + if (SYSPROF_IS_CALLGRAPH_SYMBOL (sym)) + { + SysprofCallgraph *callgraph = sysprof_callgraph_symbol_get_callgraph (sym); + AugmentWeight *sum = sysprof_callgraph_symbol_get_summary_augment (sym); + AugmentWeight *root = sysprof_callgraph_get_augment (callgraph, NULL); + + return sum->total / (double)root->total; + } + + return 0; +} + +static double +functions_get_self_fraction (GObject *item) +{ + g_autoptr(SysprofCallgraphSymbol) sym = NULL; + + g_object_get (item, "item", &sym, NULL); + + if (SYSPROF_IS_CALLGRAPH_SYMBOL (sym)) + { + SysprofCallgraph *callgraph = sysprof_callgraph_symbol_get_callgraph (sym); + AugmentWeight *sum = sysprof_callgraph_symbol_get_summary_augment (sym); + AugmentWeight *root = sysprof_callgraph_get_augment (callgraph, NULL); + + return sum->size / (double)root->total; + } + + return 0; +} + static int -sort_by_self (gconstpointer a, - gconstpointer b, - gpointer user_data) +descendants_sort_by_self (gconstpointer a, + gconstpointer b, + gpointer user_data) { SysprofCallgraphFrame *frame_a = (SysprofCallgraphFrame *)a; SysprofCallgraphFrame *frame_b = (SysprofCallgraphFrame *)b; @@ -146,9 +188,9 @@ sort_by_self (gconstpointer a, } static int -sort_by_total (gconstpointer a, - gconstpointer b, - gpointer user_data) +descendants_sort_by_total (gconstpointer a, + gconstpointer b, + gpointer user_data) { SysprofCallgraphFrame *frame_a = (SysprofCallgraphFrame *)a; SysprofCallgraphFrame *frame_b = (SysprofCallgraphFrame *)b; @@ -166,6 +208,48 @@ sort_by_total (gconstpointer a, return 0; } +static int +functions_sort_by_self (gconstpointer a, + gconstpointer b, + gpointer user_data) +{ + SysprofCallgraphSymbol *sym_a = (SysprofCallgraphSymbol *)a; + SysprofCallgraphSymbol *sym_b = (SysprofCallgraphSymbol *)b; + AugmentWeight *aug_a = sysprof_callgraph_symbol_get_summary_augment (sym_a); + AugmentWeight *aug_b = sysprof_callgraph_symbol_get_summary_augment (sym_b); + AugmentWeight *root = user_data; + double self_a = aug_a->size / (double)root->total; + double self_b = aug_b->size / (double)root->total; + + if (self_a < self_b) + return -1; + else if (self_a > self_b) + return 1; + else + return 0; +} + +static int +functions_sort_by_total (gconstpointer a, + gconstpointer b, + gpointer user_data) +{ + SysprofCallgraphSymbol *sym_a = (SysprofCallgraphSymbol *)a; + SysprofCallgraphSymbol *sym_b = (SysprofCallgraphSymbol *)b; + AugmentWeight *aug_a = sysprof_callgraph_symbol_get_summary_augment (sym_a); + AugmentWeight *aug_b = sysprof_callgraph_symbol_get_summary_augment (sym_b); + AugmentWeight *root = user_data; + double total_a = aug_a->total / (double)root->total; + double total_b = aug_b->total / (double)root->total; + + if (total_a < total_b) + return -1; + else if (total_a > total_b) + return 1; + else + return 0; +} + static void sysprof_weighted_callgraph_view_load (SysprofCallgraphView *view, SysprofCallgraph *callgraph) @@ -178,12 +262,18 @@ sysprof_weighted_callgraph_view_load (SysprofCallgraphView *view, root = sysprof_callgraph_get_augment (callgraph, NULL); - gtk_custom_sorter_set_sort_func (self->self_sorter, sort_by_self, root, NULL); - gtk_custom_sorter_set_sort_func (self->total_sorter, sort_by_total, root, NULL); + gtk_custom_sorter_set_sort_func (self->self_sorter, descendants_sort_by_self, root, NULL); + gtk_custom_sorter_set_sort_func (self->total_sorter, descendants_sort_by_total, root, NULL); + + gtk_custom_sorter_set_sort_func (self->functions_self_sorter, functions_sort_by_self, root, NULL); + gtk_custom_sorter_set_sort_func (self->functions_total_sorter, functions_sort_by_total, root, NULL); gtk_column_view_sort_by_column (SYSPROF_CALLGRAPH_VIEW (self)->column_view, self->total_column, GTK_SORT_DESCENDING); + gtk_column_view_sort_by_column (SYSPROF_CALLGRAPH_VIEW (self)->functions_column_view, + self->functions_total_column, + GTK_SORT_DESCENDING); } static void @@ -197,12 +287,21 @@ sysprof_weighted_callgraph_view_class_init (SysprofWeightedCallgraphViewClass *k callgraph_view_class->load = sysprof_weighted_callgraph_view_load; gtk_widget_class_set_template_from_resource (widget_class, "/libsysprof-gtk/sysprof-weighted-callgraph-view.ui"); + gtk_widget_class_bind_template_child (widget_class, SysprofWeightedCallgraphView, self_column); gtk_widget_class_bind_template_child (widget_class, SysprofWeightedCallgraphView, total_column); gtk_widget_class_bind_template_child (widget_class, SysprofWeightedCallgraphView, self_sorter); gtk_widget_class_bind_template_child (widget_class, SysprofWeightedCallgraphView, total_sorter); + + gtk_widget_class_bind_template_child (widget_class, SysprofWeightedCallgraphView, functions_self_column); + gtk_widget_class_bind_template_child (widget_class, SysprofWeightedCallgraphView, functions_total_column); + gtk_widget_class_bind_template_child (widget_class, SysprofWeightedCallgraphView, functions_self_sorter); + gtk_widget_class_bind_template_child (widget_class, SysprofWeightedCallgraphView, functions_total_sorter); + gtk_widget_class_bind_template_callback (widget_class, get_self_fraction); gtk_widget_class_bind_template_callback (widget_class, get_total_fraction); + gtk_widget_class_bind_template_callback (widget_class, functions_get_self_fraction); + gtk_widget_class_bind_template_callback (widget_class, functions_get_total_fraction); g_type_ensure (SYSPROF_TYPE_PROGRESS_CELL); } diff --git a/src/libsysprof-gtk/sysprof-weighted-callgraph-view.ui b/src/libsysprof-gtk/sysprof-weighted-callgraph-view.ui index 56260171..087ee6c0 100644 --- a/src/libsysprof-gtk/sysprof-weighted-callgraph-view.ui +++ b/src/libsysprof-gtk/sysprof-weighted-callgraph-view.ui @@ -65,5 +65,72 @@ + + + + + + Self + + + + + + +]]> + + + + + + + + + + + + Total + + + + + + +]]> + + + + + + + + + + + From fa35089e93fe004ba3fd4726fcb2d54c8854b710 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 12 Jun 2023 11:23:35 -0700 Subject: [PATCH 0402/1030] libsysprof-gtk: rename descendants view for legibility --- .../sysprof-callgraph-view-private.h | 2 +- src/libsysprof-gtk/sysprof-callgraph-view.c | 53 ++++++++++--------- src/libsysprof-gtk/sysprof-callgraph-view.ui | 2 +- .../sysprof-weighted-callgraph-view.c | 32 ++++++----- .../sysprof-weighted-callgraph-view.ui | 10 ++-- 5 files changed, 52 insertions(+), 47 deletions(-) diff --git a/src/libsysprof-gtk/sysprof-callgraph-view-private.h b/src/libsysprof-gtk/sysprof-callgraph-view-private.h index 031cb32d..cbee16a8 100644 --- a/src/libsysprof-gtk/sysprof-callgraph-view-private.h +++ b/src/libsysprof-gtk/sysprof-callgraph-view-private.h @@ -33,7 +33,7 @@ struct _SysprofCallgraphView SysprofDocument *document; GListModel *traceables; - GtkColumnView *column_view; + GtkColumnView *descendants_column_view; GtkColumnView *functions_column_view; GtkScrolledWindow *scrolled_window; GtkWidget *paned; diff --git a/src/libsysprof-gtk/sysprof-callgraph-view.c b/src/libsysprof-gtk/sysprof-callgraph-view.c index fd31dfd8..2d6de21a 100644 --- a/src/libsysprof-gtk/sysprof-callgraph-view.c +++ b/src/libsysprof-gtk/sysprof-callgraph-view.c @@ -152,7 +152,7 @@ sysprof_callgraph_view_class_init (SysprofCallgraphViewClass *klass) gtk_widget_class_set_template_from_resource (widget_class, "/libsysprof-gtk/sysprof-callgraph-view.ui"); gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT); - gtk_widget_class_bind_template_child (widget_class, SysprofCallgraphView, column_view); + gtk_widget_class_bind_template_child (widget_class, SysprofCallgraphView, descendants_column_view); gtk_widget_class_bind_template_child (widget_class, SysprofCallgraphView, functions_column_view); gtk_widget_class_bind_template_child (widget_class, SysprofCallgraphView, paned); gtk_widget_class_bind_template_child (widget_class, SysprofCallgraphView, scrolled_window); @@ -187,17 +187,19 @@ sysprof_callgraph_view_reload_cb (GObject *object, SysprofDocument *document = (SysprofDocument *)object; g_autoptr(SysprofCallgraphView) self = user_data; g_autoptr(SysprofCallgraph) callgraph = NULL; - g_autoptr(GtkTreeListRowSorter) sorter = NULL; - g_autoptr(GtkMultiSelection) model = NULL; - g_autoptr(GtkSingleSelection) functions_selection = NULL; - g_autoptr(GtkSortListModel) sort_model = NULL; - g_autoptr(GtkSortListModel) functions_sort_model = NULL; - g_autoptr(GListModel) functions_model = NULL; - g_autoptr(GtkTreeListModel) tree = NULL; - g_autoptr(GtkTreeListRow) first = NULL; g_autoptr(GError) error = NULL; GtkSorter *column_sorter; + g_autoptr(GtkTreeListRowSorter) descendants_sorter = NULL; + g_autoptr(GtkMultiSelection) descendants_selection = NULL; + g_autoptr(GtkSortListModel) descendants_sort_model = NULL; + g_autoptr(GtkTreeListModel) descendants_tree = NULL; + g_autoptr(GtkTreeListRow) descendants_first = NULL; + + g_autoptr(GtkSingleSelection) functions_selection = NULL; + g_autoptr(GtkSortListModel) functions_sort_model = NULL; + g_autoptr(GListModel) functions_model = NULL; + g_assert (SYSPROF_IS_DOCUMENT (document)); g_assert (G_IS_ASYNC_RESULT (result)); g_assert (SYSPROF_IS_CALLGRAPH_VIEW (self)); @@ -208,18 +210,16 @@ sysprof_callgraph_view_reload_cb (GObject *object, return; } - tree = gtk_tree_list_model_new (g_object_ref (G_LIST_MODEL (callgraph)), - FALSE, - FALSE, - sysprof_callgraph_view_create_model_func, - NULL, - NULL); - column_sorter = gtk_column_view_get_sorter (self->column_view); - sorter = gtk_tree_list_row_sorter_new (g_object_ref (column_sorter)); - sort_model = gtk_sort_list_model_new (g_object_ref (G_LIST_MODEL (tree)), - g_object_ref (GTK_SORTER (sorter))); - model = gtk_multi_selection_new (g_object_ref (G_LIST_MODEL (sort_model))); - gtk_column_view_set_model (self->column_view, GTK_SELECTION_MODEL (model)); + column_sorter = gtk_column_view_get_sorter (self->descendants_column_view); + descendants_tree = gtk_tree_list_model_new (g_object_ref (G_LIST_MODEL (callgraph)), + FALSE, FALSE, + sysprof_callgraph_view_create_model_func, + NULL, NULL); + descendants_sorter = gtk_tree_list_row_sorter_new (g_object_ref (column_sorter)); + descendants_sort_model = gtk_sort_list_model_new (g_object_ref (G_LIST_MODEL (descendants_tree)), + g_object_ref (GTK_SORTER (descendants_sorter))); + descendants_selection = gtk_multi_selection_new (g_object_ref (G_LIST_MODEL (descendants_sort_model))); + gtk_column_view_set_model (self->descendants_column_view, GTK_SELECTION_MODEL (descendants_selection)); column_sorter = gtk_column_view_get_sorter (self->functions_column_view); functions_model = sysprof_callgraph_list_symbols (callgraph); @@ -232,8 +232,8 @@ sysprof_callgraph_view_reload_cb (GObject *object, if (SYSPROF_CALLGRAPH_VIEW_GET_CLASS (self)->load) SYSPROF_CALLGRAPH_VIEW_GET_CLASS (self)->load (self, callgraph); - if ((first = gtk_tree_list_model_get_row (tree, 0))) - gtk_tree_list_row_set_expanded (first, TRUE); + if ((descendants_first = gtk_tree_list_model_get_row (descendants_tree, 0))) + gtk_tree_list_row_set_expanded (descendants_first, TRUE); } static gboolean @@ -261,7 +261,8 @@ sysprof_callgraph_view_queue_reload (SysprofCallgraphView *self) { g_assert (SYSPROF_IS_CALLGRAPH_VIEW (self)); - gtk_column_view_set_model (self->column_view, NULL); + gtk_column_view_set_model (self->descendants_column_view, NULL); + gtk_column_view_set_model (self->functions_column_view, NULL); g_clear_handle_id (&self->reload_source, g_source_remove); g_cancellable_cancel (self->cancellable); @@ -340,8 +341,8 @@ sysprof_callgraph_view_get_internal_child (GtkBuildable *buildable, GtkBuilder *builder, const char *name) { - if (g_strcmp0 (name, "column_view") == 0) - return G_OBJECT (SYSPROF_CALLGRAPH_VIEW (buildable)->column_view); + if (g_strcmp0 (name, "descendants_column_view") == 0) + return G_OBJECT (SYSPROF_CALLGRAPH_VIEW (buildable)->descendants_column_view); else if (g_strcmp0 (name, "functions_column_view") == 0) return G_OBJECT (SYSPROF_CALLGRAPH_VIEW (buildable)->functions_column_view); diff --git a/src/libsysprof-gtk/sysprof-callgraph-view.ui b/src/libsysprof-gtk/sysprof-callgraph-view.ui index 6b7e0469..ec6f5a33 100644 --- a/src/libsysprof-gtk/sysprof-callgraph-view.ui +++ b/src/libsysprof-gtk/sysprof-callgraph-view.ui @@ -52,7 +52,7 @@ - + diff --git a/src/libsysprof-gtk/sysprof-weighted-callgraph-view.c b/src/libsysprof-gtk/sysprof-weighted-callgraph-view.c index 74cc5e26..098213a2 100644 --- a/src/libsysprof-gtk/sysprof-weighted-callgraph-view.c +++ b/src/libsysprof-gtk/sysprof-weighted-callgraph-view.c @@ -28,10 +28,10 @@ struct _SysprofWeightedCallgraphView { SysprofCallgraphView parent_instance; - GtkColumnViewColumn *self_column; - GtkColumnViewColumn *total_column; - GtkCustomSorter *self_sorter; - GtkCustomSorter *total_sorter; + GtkColumnViewColumn *descendants_self_column; + GtkColumnViewColumn *descendants_total_column; + GtkCustomSorter *descendants_self_sorter; + GtkCustomSorter *descendants_total_sorter; GtkColumnViewColumn *functions_self_column; GtkColumnViewColumn *functions_total_column; @@ -262,14 +262,18 @@ sysprof_weighted_callgraph_view_load (SysprofCallgraphView *view, root = sysprof_callgraph_get_augment (callgraph, NULL); - gtk_custom_sorter_set_sort_func (self->self_sorter, descendants_sort_by_self, root, NULL); - gtk_custom_sorter_set_sort_func (self->total_sorter, descendants_sort_by_total, root, NULL); + gtk_custom_sorter_set_sort_func (self->descendants_self_sorter, + descendants_sort_by_self, root, NULL); + gtk_custom_sorter_set_sort_func (self->descendants_total_sorter, + descendants_sort_by_total, root, NULL); - gtk_custom_sorter_set_sort_func (self->functions_self_sorter, functions_sort_by_self, root, NULL); - gtk_custom_sorter_set_sort_func (self->functions_total_sorter, functions_sort_by_total, root, NULL); + gtk_custom_sorter_set_sort_func (self->functions_self_sorter, + functions_sort_by_self, root, NULL); + gtk_custom_sorter_set_sort_func (self->functions_total_sorter, + functions_sort_by_total, root, NULL); - gtk_column_view_sort_by_column (SYSPROF_CALLGRAPH_VIEW (self)->column_view, - self->total_column, + gtk_column_view_sort_by_column (SYSPROF_CALLGRAPH_VIEW (self)->descendants_column_view, + self->descendants_total_column, GTK_SORT_DESCENDING); gtk_column_view_sort_by_column (SYSPROF_CALLGRAPH_VIEW (self)->functions_column_view, self->functions_total_column, @@ -288,10 +292,10 @@ sysprof_weighted_callgraph_view_class_init (SysprofWeightedCallgraphViewClass *k gtk_widget_class_set_template_from_resource (widget_class, "/libsysprof-gtk/sysprof-weighted-callgraph-view.ui"); - gtk_widget_class_bind_template_child (widget_class, SysprofWeightedCallgraphView, self_column); - gtk_widget_class_bind_template_child (widget_class, SysprofWeightedCallgraphView, total_column); - gtk_widget_class_bind_template_child (widget_class, SysprofWeightedCallgraphView, self_sorter); - gtk_widget_class_bind_template_child (widget_class, SysprofWeightedCallgraphView, total_sorter); + gtk_widget_class_bind_template_child (widget_class, SysprofWeightedCallgraphView, descendants_self_column); + gtk_widget_class_bind_template_child (widget_class, SysprofWeightedCallgraphView, descendants_total_column); + gtk_widget_class_bind_template_child (widget_class, SysprofWeightedCallgraphView, descendants_self_sorter); + gtk_widget_class_bind_template_child (widget_class, SysprofWeightedCallgraphView, descendants_total_sorter); gtk_widget_class_bind_template_child (widget_class, SysprofWeightedCallgraphView, functions_self_column); gtk_widget_class_bind_template_child (widget_class, SysprofWeightedCallgraphView, functions_total_column); diff --git a/src/libsysprof-gtk/sysprof-weighted-callgraph-view.ui b/src/libsysprof-gtk/sysprof-weighted-callgraph-view.ui index 087ee6c0..99c50d63 100644 --- a/src/libsysprof-gtk/sysprof-weighted-callgraph-view.ui +++ b/src/libsysprof-gtk/sysprof-weighted-callgraph-view.ui @@ -1,10 +1,10 @@ +]]> + + + + + + + + Depth + true + + + + + + ]]> From a3df31c246203e9c0b69326ff3a57b79abe01258 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 12 Jun 2023 17:21:28 -0700 Subject: [PATCH 0423/1030] libsysprof-analyze: include binary path basename in hash This helps improve the chances that we get a match on "libfoo.so" and the symbol name within it. --- src/libsysprof-analyze/sysprof-symbol.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-symbol.c b/src/libsysprof-analyze/sysprof-symbol.c index 1d44209e..48936c62 100644 --- a/src/libsysprof-analyze/sysprof-symbol.c +++ b/src/libsysprof-analyze/sysprof-symbol.c @@ -146,6 +146,21 @@ _sysprof_symbol_new (GRefString *name, self->end_address = end_address; self->hash = g_str_hash (name); + /* If we got a path for the symbol, add that to the hash so that we + * can be sure that we're working with a symbol in the same file when + * there are collisions. + * + * That way, we have a chance of joining symbols from different runtimes + * and/or containers, but only if they are reasonably the same ABI. + */ + if (binary_path != NULL) + { + const char *base = strrchr (binary_path, '/'); + + if (base != NULL) + self->hash ^= g_str_hash (base); + } + return self; } From 465b0e1613e752483abffba40b3fd5e1d7bea006 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 12 Jun 2023 17:21:41 -0700 Subject: [PATCH 0424/1030] libsysprof-analyze: add sysprof_symbol_hash() for hash tables --- src/libsysprof-analyze/sysprof-symbol.c | 6 ++++++ src/libsysprof-analyze/sysprof-symbol.h | 2 ++ 2 files changed, 8 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-symbol.c b/src/libsysprof-analyze/sysprof-symbol.c index 48936c62..e367916e 100644 --- a/src/libsysprof-analyze/sysprof-symbol.c +++ b/src/libsysprof-analyze/sysprof-symbol.c @@ -170,3 +170,9 @@ sysprof_symbol_equal (const SysprofSymbol *a, { return _sysprof_symbol_equal (a, b); } + +guint +sysprof_symbol_hash (const SysprofSymbol *self) +{ + return self->hash; +} diff --git a/src/libsysprof-analyze/sysprof-symbol.h b/src/libsysprof-analyze/sysprof-symbol.h index 01e0522b..6a51603b 100644 --- a/src/libsysprof-analyze/sysprof-symbol.h +++ b/src/libsysprof-analyze/sysprof-symbol.h @@ -39,6 +39,8 @@ const char *sysprof_symbol_get_binary_nick (SysprofSymbol *self); SYSPROF_AVAILABLE_IN_ALL const char *sysprof_symbol_get_binary_path (SysprofSymbol *self); SYSPROF_AVAILABLE_IN_ALL +guint sysprof_symbol_hash (const SysprofSymbol *self); +SYSPROF_AVAILABLE_IN_ALL gboolean sysprof_symbol_equal (const SysprofSymbol *a, const SysprofSymbol *b); From 90fdd7f842b0d77b5aedbaa3514bccd4c174fb1f Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 12 Jun 2023 17:22:17 -0700 Subject: [PATCH 0425/1030] libsysprof-analyze: use symbol hash for better coalescing We want to increase how much the symbols get coalesced across processes so long as they are reasonably the same symbol. --- src/libsysprof-analyze/sysprof-callgraph.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libsysprof-analyze/sysprof-callgraph.c b/src/libsysprof-analyze/sysprof-callgraph.c index 3a83114b..1c9e178b 100644 --- a/src/libsysprof-analyze/sysprof-callgraph.c +++ b/src/libsysprof-analyze/sysprof-callgraph.c @@ -388,7 +388,10 @@ _sysprof_callgraph_new_async (SysprofDocument *document, self->augment_func = augment_func; self->augment_func_data = augment_func_data; self->augment_func_data_destroy = augment_func_data_destroy; - self->symbol_to_summary = g_hash_table_new_full (NULL, NULL, NULL, summary_free); + self->symbol_to_summary = g_hash_table_new_full ((GHashFunc)sysprof_symbol_hash, + (GEqualFunc)sysprof_symbol_equal, + NULL, + summary_free); self->symbols = g_ptr_array_new (); self->root.summary = sysprof_callgraph_get_summary (self, everything); From 8af3cd896af9fa9727c574b4ad772d8b482c928d Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 12 Jun 2023 22:59:11 -0700 Subject: [PATCH 0426/1030] libsysprof-analyze: add descendants generation We still have some work to do here so it doesn't mess up the summary augmentation, but this is a start on re-generating the callgraphs for a specific symbol. --- src/libsysprof-analyze/meson.build | 1 + .../sysprof-callgraph-frame-private.h | 1 + .../sysprof-callgraph-frame.c | 6 +- src/libsysprof-analyze/sysprof-callgraph.c | 55 +++- src/libsysprof-analyze/sysprof-callgraph.h | 10 + .../sysprof-descendants-model-private.h | 35 +++ .../sysprof-descendants-model.c | 238 ++++++++++++++++++ 7 files changed, 343 insertions(+), 3 deletions(-) create mode 100644 src/libsysprof-analyze/sysprof-descendants-model-private.h create mode 100644 src/libsysprof-analyze/sysprof-descendants-model.c diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build index ae7bd11f..723dc595 100644 --- a/src/libsysprof-analyze/meson.build +++ b/src/libsysprof-analyze/meson.build @@ -35,6 +35,7 @@ libsysprof_analyze_public_sources = [ libsysprof_analyze_private_sources = [ 'sysprof-address-layout.c', + 'sysprof-descendants-model.c', 'sysprof-document-bitset-index.c', 'sysprof-document-symbols.c', 'sysprof-elf.c', diff --git a/src/libsysprof-analyze/sysprof-callgraph-frame-private.h b/src/libsysprof-analyze/sysprof-callgraph-frame-private.h index 0d2814e5..2c58fbd2 100644 --- a/src/libsysprof-analyze/sysprof-callgraph-frame-private.h +++ b/src/libsysprof-analyze/sysprof-callgraph-frame-private.h @@ -26,6 +26,7 @@ G_BEGIN_DECLS SysprofCallgraphFrame *_sysprof_callgraph_frame_new_for_node (SysprofCallgraph *callgraph, + GObject *owner, SysprofCallgraphNode *node); G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-callgraph-frame.c b/src/libsysprof-analyze/sysprof-callgraph-frame.c index 0bca4418..ffb0572e 100644 --- a/src/libsysprof-analyze/sysprof-callgraph-frame.c +++ b/src/libsysprof-analyze/sysprof-callgraph-frame.c @@ -35,6 +35,7 @@ struct _SysprofCallgraphFrame { GObject parent_instance; SysprofCallgraph *callgraph; + GObject *owner; SysprofCallgraphNode *node; guint n_children; }; @@ -79,7 +80,7 @@ sysprof_callgraph_frame_get_item (GListModel *model, if (iter == NULL) return NULL; - return _sysprof_callgraph_frame_new_for_node (self->callgraph, iter); + return _sysprof_callgraph_frame_new_for_node (self->callgraph, self->owner, iter); } static void @@ -101,6 +102,7 @@ sysprof_callgraph_frame_finalize (GObject *object) SysprofCallgraphFrame *self = (SysprofCallgraphFrame *)object; g_clear_weak_pointer (&self->callgraph); + g_clear_object (&self->owner); self->node = NULL; G_OBJECT_CLASS (sysprof_callgraph_frame_parent_class)->finalize (object); @@ -157,6 +159,7 @@ sysprof_callgraph_frame_init (SysprofCallgraphFrame *self) SysprofCallgraphFrame * _sysprof_callgraph_frame_new_for_node (SysprofCallgraph *callgraph, + GObject *owner, SysprofCallgraphNode *node) { SysprofCallgraphFrame *self; @@ -166,6 +169,7 @@ _sysprof_callgraph_frame_new_for_node (SysprofCallgraph *callgraph, self = g_object_new (SYSPROF_TYPE_CALLGRAPH_FRAME, NULL); g_set_weak_pointer (&self->callgraph, callgraph); + g_set_object (&self->owner, owner); self->node = node; for (const SysprofCallgraphNode *iter = node->children; diff --git a/src/libsysprof-analyze/sysprof-callgraph.c b/src/libsysprof-analyze/sysprof-callgraph.c index 1c9e178b..a111d9fe 100644 --- a/src/libsysprof-analyze/sysprof-callgraph.c +++ b/src/libsysprof-analyze/sysprof-callgraph.c @@ -23,6 +23,7 @@ #include "sysprof-callgraph-private.h" #include "sysprof-callgraph-frame-private.h" #include "sysprof-callgraph-symbol-private.h" +#include "sysprof-descendants-model-private.h" #include "sysprof-document-bitset-index-private.h" #include "sysprof-document-private.h" #include "sysprof-document-traceable.h" @@ -53,7 +54,7 @@ sysprof_callgraph_get_item (GListModel *model, if (position > 0) return NULL; - return _sysprof_callgraph_frame_new_for_node (self, &self->root); + return _sysprof_callgraph_frame_new_for_node (self, NULL, &self->root); } static void @@ -199,7 +200,8 @@ sysprof_callgraph_populate_callers (SysprofCallgraph *self, SysprofSymbol *parent_symbol = iter->parent->summary->symbol; guint pos; - if (!g_ptr_array_find (iter->summary->callers, parent_symbol, &pos)) + if (!(parent_symbol->is_process || parent_symbol->is_everything) && + !g_ptr_array_find (iter->summary->callers, parent_symbol, &pos)) g_ptr_array_add (iter->summary->callers, parent_symbol); } } @@ -532,3 +534,52 @@ sysprof_callgraph_list_symbols (SysprofCallgraph *self) return _sysprof_callgraph_symbol_list_model_new (self, self->symbols); } + +static void +sysprof_callgraph_descendants_worker (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) +{ + SysprofCallgraph *self = source_object; + SysprofSymbol *symbol = task_data; + + g_assert (G_IS_TASK (task)); + g_assert (SYSPROF_IS_CALLGRAPH (self)); + g_assert (SYSPROF_IS_SYMBOL (symbol)); + g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); + + g_task_return_pointer (task, + _sysprof_descendants_model_new (self, symbol), + g_object_unref); +} + +void +sysprof_callgraph_descendants_async (SysprofCallgraph *self, + SysprofSymbol *symbol, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_autoptr(GTask) task = NULL; + + g_return_if_fail (SYSPROF_IS_CALLGRAPH (self)); + g_return_if_fail (SYSPROF_IS_SYMBOL (symbol)); + g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); + + task = g_task_new (self, cancellable, callback, user_data); + g_task_set_source_tag (task, sysprof_callgraph_descendants_async); + g_task_set_task_data (task, g_object_ref (symbol), g_object_unref); + g_task_run_in_thread (task, sysprof_callgraph_descendants_worker); +} + +GListModel * +sysprof_callgraph_descendants_finish (SysprofCallgraph *self, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (SYSPROF_IS_CALLGRAPH (self), NULL); + g_return_val_if_fail (G_IS_TASK (result), NULL); + + return g_task_propagate_pointer (G_TASK (result), error); +} diff --git a/src/libsysprof-analyze/sysprof-callgraph.h b/src/libsysprof-analyze/sysprof-callgraph.h index 5f3afe3c..5e5e0cb0 100644 --- a/src/libsysprof-analyze/sysprof-callgraph.h +++ b/src/libsysprof-analyze/sysprof-callgraph.h @@ -66,6 +66,16 @@ SYSPROF_AVAILABLE_IN_ALL GListModel *sysprof_callgraph_list_traceables_for_symbol (SysprofCallgraph *self, SysprofSymbol *symbol); SYSPROF_AVAILABLE_IN_ALL +void sysprof_callgraph_descendants_async (SysprofCallgraph *self, + SysprofSymbol *symbol, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +SYSPROF_AVAILABLE_IN_ALL +GListModel *sysprof_callgraph_descendants_finish (SysprofCallgraph *self, + GAsyncResult *result, + GError **error); +SYSPROF_AVAILABLE_IN_ALL gpointer sysprof_callgraph_get_augment (SysprofCallgraph *self, SysprofCallgraphNode *node); SYSPROF_AVAILABLE_IN_ALL diff --git a/src/libsysprof-analyze/sysprof-descendants-model-private.h b/src/libsysprof-analyze/sysprof-descendants-model-private.h new file mode 100644 index 00000000..9560ce6b --- /dev/null +++ b/src/libsysprof-analyze/sysprof-descendants-model-private.h @@ -0,0 +1,35 @@ +/* sysprof-descendants-model-private.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-callgraph.h" +#include "sysprof-symbol.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_DESCENDANTS_MODEL (sysprof_descendants_model_get_type()) + +G_DECLARE_FINAL_TYPE (SysprofDescendantsModel, sysprof_descendants_model, SYSPROF, DESCENDANTS_MODEL, GObject) + +GListModel *_sysprof_descendants_model_new (SysprofCallgraph *callgraph, + SysprofSymbol *symbol); + +G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-descendants-model.c b/src/libsysprof-analyze/sysprof-descendants-model.c new file mode 100644 index 00000000..d019418d --- /dev/null +++ b/src/libsysprof-analyze/sysprof-descendants-model.c @@ -0,0 +1,238 @@ +/* sysprof-descendants-model.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-callgraph-private.h" +#include "sysprof-callgraph-frame-private.h" +#include "sysprof-descendants-model-private.h" +#include "sysprof-symbol-private.h" + +#define MAX_STACK_DEPTH 128 + +struct _SysprofDescendantsModel +{ + GObject parent_instance; + SysprofCallgraph *callgraph; + SysprofSymbol *symbol; + SysprofCallgraphNode root; +}; + +static GType +sysprof_descendants_model_get_item_type (GListModel *model) +{ + return SYSPROF_TYPE_CALLGRAPH_FRAME; +} + +static guint +sysprof_descendants_model_get_n_items (GListModel *model) +{ + return 1; +} + +static gpointer +sysprof_descendants_model_get_item (GListModel *model, + guint position) +{ + SysprofDescendantsModel *self = SYSPROF_DESCENDANTS_MODEL (model); + + if (position != 0) + return NULL; + + g_warning ("TODO: Need to get proper ownership for node and proper summary"); + + return _sysprof_callgraph_frame_new_for_node (self->callgraph, G_OBJECT (self), &self->root); +} + +static void +list_model_iface_init (GListModelInterface *iface) +{ + iface->get_item_type = sysprof_descendants_model_get_item_type; + iface->get_n_items = sysprof_descendants_model_get_n_items; + iface->get_item = sysprof_descendants_model_get_item; +} + +G_DEFINE_FINAL_TYPE_WITH_CODE (SysprofDescendantsModel, sysprof_descendants_model, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init)) + +static void +sysprof_descendants_model_finalize (GObject *object) +{ + SysprofDescendantsModel *self = (SysprofDescendantsModel *)object; + + g_clear_object (&self->callgraph); + g_clear_object (&self->symbol); + + G_OBJECT_CLASS (sysprof_descendants_model_parent_class)->finalize (object); +} + +static void +sysprof_descendants_model_class_init (SysprofDescendantsModelClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = sysprof_descendants_model_finalize; +} + +static void +sysprof_descendants_model_init (SysprofDescendantsModel *self) +{ +} + +static SysprofCallgraphNode * +sysprof_descendants_model_add_trace (SysprofDescendantsModel *self, + SysprofSymbol **symbols, + guint n_symbols) +{ + SysprofCallgraphNode *parent = NULL; + + g_assert (SYSPROF_IS_DESCENDANTS_MODEL (self)); + g_assert (symbols != NULL); + g_assert (n_symbols > 0); + + parent = &self->root; + + for (guint i = n_symbols; i > 0; i--) + { + SysprofSymbol *symbol = symbols[i-1]; + SysprofCallgraphNode *node = NULL; + SysprofCallgraphSummary *summary; + + /* Try to find @symbol within the children of @parent */ + for (SysprofCallgraphNode *iter = parent->children; + iter != NULL; + iter = iter->next) + { + g_assert (iter != NULL); + g_assert (iter->summary != NULL); + g_assert (iter->summary->symbol != NULL); + g_assert (symbol != NULL); + + if (_sysprof_symbol_equal (iter->summary->symbol, symbol)) + { + node = iter; + goto next_symbol; + } + } + + if (!(summary = g_hash_table_lookup (self->callgraph->symbol_to_summary, symbol))) + { + node = parent; + goto next_symbol; + } + + /* Otherwise create a new node */ + node = g_new0 (SysprofCallgraphNode, 1); + node->summary = summary; + node->parent = parent; + node->next = parent->children; + if (parent->children) + parent->children->prev = node; + parent->children = node; + + g_assert (node->summary != NULL); + + next_symbol: + parent = node; + } + + return parent; +} + +static void +sysprof_descendants_model_add_traceable (SysprofDescendantsModel *self, + SysprofDocument *document, + SysprofDocumentTraceable *traceable, + SysprofSymbol *from_symbol) +{ + SysprofAddressContext final_context; + SysprofSymbol **symbols; + guint stack_depth; + guint n_symbols; + + g_assert (SYSPROF_IS_DESCENDANTS_MODEL (self)); + g_assert (SYSPROF_IS_DOCUMENT (document)); + g_assert (SYSPROF_IS_DOCUMENT_TRACEABLE (traceable)); + g_assert (SYSPROF_IS_SYMBOL (from_symbol)); + + stack_depth = MIN (MAX_STACK_DEPTH, sysprof_document_traceable_get_stack_depth (traceable)); + symbols = g_alloca (sizeof (SysprofSymbol *) * stack_depth); + n_symbols = sysprof_document_symbolize_traceable (document, traceable, symbols, stack_depth, &final_context); + + for (guint i = n_symbols; i > 0; i--) + { + SysprofSymbol *symbol = symbols[i-1]; + + n_symbols--; + + if (_sysprof_symbol_equal (symbol, from_symbol)) + break; + } + + if (n_symbols > 0) + { + SysprofCallgraphNode *node; + + node = sysprof_descendants_model_add_trace (self, symbols, n_symbols); + + /* TODO: This will fuck up summaries */ + + if (node && self->callgraph->augment_func) + self->callgraph->augment_func (self->callgraph, + node, + SYSPROF_DOCUMENT_FRAME (traceable), + self->callgraph->augment_func_data); + } +} + +GListModel * +_sysprof_descendants_model_new (SysprofCallgraph *callgraph, + SysprofSymbol *symbol) +{ + SysprofDescendantsModel *self; + g_autoptr(SysprofDocument) document = NULL; + g_autoptr(GListModel) model = NULL; + guint n_items; + + g_return_val_if_fail (SYSPROF_IS_CALLGRAPH (callgraph), NULL); + g_return_val_if_fail (SYSPROF_IS_SYMBOL (symbol), NULL); + + model = sysprof_callgraph_list_traceables_for_symbol (callgraph, symbol); + document = g_object_ref (callgraph->document); + + self = g_object_new (SYSPROF_TYPE_DESCENDANTS_MODEL, NULL); + self->callgraph = g_object_ref (callgraph); + self->symbol = g_object_ref (symbol); + self->root.summary = g_hash_table_lookup (callgraph->symbol_to_summary, symbol); + + g_assert (self->root.summary != NULL); + g_assert (_sysprof_symbol_equal (self->root.summary->symbol, symbol)); + + n_items = g_list_model_get_n_items (model); + + for (guint i = 0; i < n_items; i++) + { + g_autoptr(SysprofDocumentTraceable) traceable = g_list_model_get_item (model, i); + + sysprof_descendants_model_add_traceable (self, document, traceable, symbol); + } + + return G_LIST_MODEL (self); +} From ea297b79ba8977e639c8925ed5b6dd79c459a827 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 12 Jun 2023 22:59:42 -0700 Subject: [PATCH 0427/1030] libsysprof-gtk: show descendents when caller selection changes --- src/libsysprof-gtk/sysprof-callgraph-view.c | 56 ++++++++++++++++++++- 1 file changed, 54 insertions(+), 2 deletions(-) diff --git a/src/libsysprof-gtk/sysprof-callgraph-view.c b/src/libsysprof-gtk/sysprof-callgraph-view.c index d503d24d..c6bbfd1a 100644 --- a/src/libsysprof-gtk/sysprof-callgraph-view.c +++ b/src/libsysprof-gtk/sysprof-callgraph-view.c @@ -33,12 +33,58 @@ enum { }; static void buildable_iface_init (GtkBuildableIface *iface); +static GListModel *sysprof_callgraph_view_create_model_func (gpointer item, + gpointer user_data); +static void descendants_selection_changed_cb (SysprofCallgraphView *self, + guint position, + guint n_items, + GtkSingleSelection *single); G_DEFINE_ABSTRACT_TYPE_WITH_CODE (SysprofCallgraphView, sysprof_callgraph_view, GTK_TYPE_WIDGET, G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, buildable_iface_init)) static GParamSpec *properties [N_PROPS]; +static void +sysprof_callgraph_view_descendants_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + SysprofCallgraph *callgraph = (SysprofCallgraph *)object; + g_autoptr(SysprofCallgraphView) self = user_data; + g_autoptr(GListModel) model = NULL; + g_autoptr(GtkTreeListRowSorter) descendants_sorter = NULL; + g_autoptr(GtkSingleSelection) descendants_selection = NULL; + g_autoptr(GtkSortListModel) descendants_sort_model = NULL; + g_autoptr(GtkTreeListModel) descendants_tree = NULL; + g_autoptr(GtkTreeListRow) descendants_first = NULL; + g_autoptr(GError) error = NULL; + GtkSorter *column_sorter; + + g_assert (SYSPROF_IS_CALLGRAPH (callgraph)); + g_assert (G_IS_ASYNC_RESULT (result)); + g_assert (SYSPROF_IS_CALLGRAPH_VIEW (self)); + + if (!(model = sysprof_callgraph_descendants_finish (callgraph, result, &error))) + return; + + column_sorter = gtk_column_view_get_sorter (self->descendants_column_view); + descendants_tree = gtk_tree_list_model_new (g_object_ref (model), + FALSE, FALSE, + sysprof_callgraph_view_create_model_func, + NULL, NULL); + descendants_sorter = gtk_tree_list_row_sorter_new (g_object_ref (column_sorter)); + descendants_sort_model = gtk_sort_list_model_new (g_object_ref (G_LIST_MODEL (descendants_tree)), + g_object_ref (GTK_SORTER (descendants_sorter))); + descendants_selection = gtk_single_selection_new (g_object_ref (G_LIST_MODEL (descendants_sort_model))); + g_signal_connect_object (descendants_selection, + "selection-changed", + G_CALLBACK (descendants_selection_changed_cb), + self, + G_CONNECT_SWAPPED); + gtk_column_view_set_model (self->descendants_column_view, GTK_SELECTION_MODEL (descendants_selection)); +} + static void callers_selection_changed_cb (SysprofCallgraphView *self, guint position, @@ -55,8 +101,14 @@ callers_selection_changed_cb (SysprofCallgraphView *self, SysprofCallgraphSymbol *sym = SYSPROF_CALLGRAPH_SYMBOL (object); SysprofSymbol *symbol = sysprof_callgraph_symbol_get_symbol (sym); - g_print ("Caller %s selected.\n", + g_debug ("Select %s as root callgraph node", sysprof_symbol_get_name (symbol)); + + sysprof_callgraph_descendants_async (self->callgraph, + symbol, + NULL, + sysprof_callgraph_view_descendants_cb, + g_object_ref (self)); } } @@ -421,7 +473,7 @@ sysprof_callgraph_view_reload_cb (GObject *object, NULL, NULL); descendants_sorter = gtk_tree_list_row_sorter_new (g_object_ref (column_sorter)); descendants_sort_model = gtk_sort_list_model_new (g_object_ref (G_LIST_MODEL (descendants_tree)), - g_object_ref (GTK_SORTER (descendants_sorter))); + g_object_ref (GTK_SORTER (descendants_sorter))); descendants_selection = gtk_single_selection_new (g_object_ref (G_LIST_MODEL (descendants_sort_model))); g_signal_connect_object (descendants_selection, "selection-changed", From ef11121ff6c23254cc04598312c2071bc7ff3b91 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 12 Jun 2023 23:01:22 -0700 Subject: [PATCH 0428/1030] libsysprof-analyze: mark fallback symbol is_process --- src/libsysprof-analyze/sysprof-process-info.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libsysprof-analyze/sysprof-process-info.c b/src/libsysprof-analyze/sysprof-process-info.c index e0346175..7e0de75f 100644 --- a/src/libsysprof-analyze/sysprof-process-info.c +++ b/src/libsysprof-analyze/sysprof-process-info.c @@ -43,6 +43,7 @@ sysprof_process_info_new (SysprofMountNamespace *mount_namespace, self->symbol_cache = sysprof_symbol_cache_new (); self->mount_namespace = mount_namespace; self->fallback_symbol = _sysprof_symbol_new (g_ref_string_new (symname), NULL, NULL, 0, 0); + self->fallback_symbol->is_process = TRUE; return self; } From ee5ff400e7d279407f1e50af5f40d9706e27072f Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 13 Jun 2023 10:04:44 -0700 Subject: [PATCH 0429/1030] libsysprof-gtk: use shortcut controller for actions --- src/libsysprof-gtk/sysprof-callgraph-view.c | 27 ------------------- src/libsysprof-gtk/sysprof-callgraph-view.ui | 28 ++++++++++++++++---- 2 files changed, 23 insertions(+), 32 deletions(-) diff --git a/src/libsysprof-gtk/sysprof-callgraph-view.c b/src/libsysprof-gtk/sysprof-callgraph-view.c index c6bbfd1a..7c0f66eb 100644 --- a/src/libsysprof-gtk/sysprof-callgraph-view.c +++ b/src/libsysprof-gtk/sysprof-callgraph-view.c @@ -227,32 +227,6 @@ functions_selection_changed_cb (SysprofCallgraphView *self, } } -static gboolean -sysprof_callgraph_view_key_pressed_cb (GtkTreeExpander *expander, - guint keyval, - guint keycode, - GdkModifierType state, - GtkEventControllerKey *controller) -{ - GtkTreeListRow *row; - - g_assert (GTK_IS_TREE_EXPANDER (expander)); - g_assert (GTK_IS_EVENT_CONTROLLER_KEY (controller)); - - row = gtk_tree_expander_get_list_row (expander); - - if (keyval == GDK_KEY_space) - gtk_tree_list_row_set_expanded (row, !gtk_tree_list_row_get_expanded (row)); - else if (keyval == GDK_KEY_Right) - gtk_tree_list_row_set_expanded (row, TRUE); - else if (keyval == GDK_KEY_Left) - gtk_tree_list_row_set_expanded (row, FALSE); - else - return FALSE; - - return TRUE; -} - static char * format_time_offset (gpointer cell) { @@ -385,7 +359,6 @@ sysprof_callgraph_view_class_init (SysprofCallgraphViewClass *klass) gtk_widget_class_bind_template_child (widget_class, SysprofCallgraphView, scrolled_window); gtk_widget_class_bind_template_child (widget_class, SysprofCallgraphView, traceable_column_view); gtk_widget_class_bind_template_child (widget_class, SysprofCallgraphView, traceables_column_view); - gtk_widget_class_bind_template_callback (widget_class, sysprof_callgraph_view_key_pressed_cb); gtk_widget_class_bind_template_callback (widget_class, format_time_offset); klass->augment_size = GLIB_SIZEOF_VOID_P; diff --git a/src/libsysprof-gtk/sysprof-callgraph-view.ui b/src/libsysprof-gtk/sysprof-callgraph-view.ui index b3bca3af..5c833fcb 100644 --- a/src/libsysprof-gtk/sysprof-callgraph-view.ui +++ b/src/libsysprof-gtk/sysprof-callgraph-view.ui @@ -124,14 +124,32 @@ - ]]> - + ]]> @@ -101,8 +100,7 @@ -]]> - +]]> @@ -213,8 +211,7 @@ -]]> - +]]> @@ -259,8 +256,7 @@ -]]> - +]]> @@ -289,8 +285,7 @@ -]]> - +]]> @@ -320,34 +315,33 @@ -]]> - - - - - - - - - - - - true - true - - - - - true - - - Stack Trace - true - - - + + + + + + + + + + + true + true + + + + + true + + + Stack Trace + true + + + -]]> - +]]> @@ -397,8 +390,7 @@ -]]> - +]]> From 93aadbc035309b1c85528d3172db7178493e60ea Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 14 Jun 2023 13:54:41 -0700 Subject: [PATCH 0446/1030] libsysprof-gtk: make list of traceables smaller It's not that important, only that we can walk through them with the keyboard in an easier fashion than say, GtkDropDown. --- src/libsysprof-gtk/sysprof-callgraph-view.ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libsysprof-gtk/sysprof-callgraph-view.ui b/src/libsysprof-gtk/sysprof-callgraph-view.ui index 07fbb9d9..b1034455 100644 --- a/src/libsysprof-gtk/sysprof-callgraph-view.ui +++ b/src/libsysprof-gtk/sysprof-callgraph-view.ui @@ -226,7 +226,7 @@ vertical - true + 100 + + + Start + + + + + + +]]> + + + + + + + + Duration + + + + + + +]]> + + + + + + + + Group + + + + + + +]]> + + + + + + + + Type + + + + + + +]]> + + + + + + + + Description + true + + + + + + +]]> + + + + + + + + + + + + + diff --git a/src/libsysprof-gtk/tests/meson.build b/src/libsysprof-gtk/tests/meson.build index 8c3c93e2..f7692998 100644 --- a/src/libsysprof-gtk/tests/meson.build +++ b/src/libsysprof-gtk/tests/meson.build @@ -13,6 +13,7 @@ libsysprof_gtk_testsuite_c_args = [ libsysprof_gtk_testsuite = { 'test-callgraph' : {'skip': true}, + 'test-mark-table' : {'skip': true}, } libsysprof_gtk_testsuite_deps = [ diff --git a/src/libsysprof-gtk/tests/test-mark-table.c b/src/libsysprof-gtk/tests/test-mark-table.c new file mode 100644 index 00000000..e3d3734a --- /dev/null +++ b/src/libsysprof-gtk/tests/test-mark-table.c @@ -0,0 +1,94 @@ +/* test-callgraph.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include +#include +#include +#include + +static GMainLoop *main_loop; +static char *filename; +static const GOptionEntry entries[] = { + { 0 } +}; + +int +main (int argc, + char *argv[]) +{ + g_autoptr(GOptionContext) context = g_option_context_new ("- show a callgraph"); + g_autoptr(SysprofDocumentLoader) loader = NULL; + g_autoptr(SysprofDocument) document = NULL; + g_autoptr(SysprofSession) session = NULL; + g_autoptr(GError) error = NULL; + SysprofMarkTable *table; + GtkWindow *window; + + sysprof_clock_init (); + + gtk_init (); + adw_init (); + + g_option_context_add_main_entries (context, entries, NULL); + if (!g_option_context_parse (context, &argc, &argv, &error)) + { + g_printerr ("%s\n", error->message); + return 1; + } + + if (argc != 2) + { + g_print ("usage: %s [OPTIONS] CAPTURE_FILE\n", argv[0]); + return 1; + } + + filename = argv[1]; + + main_loop = g_main_loop_new (NULL, FALSE); + + loader = sysprof_document_loader_new (filename); + sysprof_document_loader_set_symbolizer (loader, sysprof_no_symbolizer_get ()); + + g_print ("Loading %s, ignoring embedded symbols...\n", filename); + if (!(document = sysprof_document_loader_load (loader, NULL, &error))) + g_error ("Failed to load document: %s", error->message); + + session = sysprof_session_new (document); + + window = g_object_new (GTK_TYPE_WINDOW, + "default-width", 800, + "default-height", 600, + NULL); + table = g_object_new (SYSPROF_TYPE_MARK_TABLE, + "session", session, + NULL); + g_signal_connect_swapped (window, + "close-request", + G_CALLBACK (g_main_loop_quit), + main_loop); + gtk_window_set_child (window, GTK_WIDGET (table)); + gtk_window_present (window); + + g_main_loop_run (main_loop); + + return 0; +} From 4551b7871cefcba76de5e904ca4ffa3d725a1b9c Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 14 Jun 2023 17:15:55 -0700 Subject: [PATCH 0457/1030] libsysprof-gtk: set single-line-mode --- src/libsysprof-gtk/sysprof-mark-table.ui | 3 +++ src/libsysprof-gtk/sysprof-time-label.c | 1 + 2 files changed, 4 insertions(+) diff --git a/src/libsysprof-gtk/sysprof-mark-table.ui b/src/libsysprof-gtk/sysprof-mark-table.ui index b2d0b53c..07ab3894 100644 --- a/src/libsysprof-gtk/sysprof-mark-table.ui +++ b/src/libsysprof-gtk/sysprof-mark-table.ui @@ -76,6 +76,7 @@ 0 + true GtkListItem @@ -103,6 +104,7 @@ 0 + true GtkListItem @@ -131,6 +133,7 @@ 0 + true GtkListItem diff --git a/src/libsysprof-gtk/sysprof-time-label.c b/src/libsysprof-gtk/sysprof-time-label.c index 56760ada..39ec499e 100644 --- a/src/libsysprof-gtk/sysprof-time-label.c +++ b/src/libsysprof-gtk/sysprof-time-label.c @@ -139,6 +139,7 @@ sysprof_time_label_init (SysprofTimeLabel *self) { self->label = g_object_new (GTK_TYPE_LABEL, "xalign", 1.f, + "single-line-mode", TRUE, NULL); gtk_widget_set_parent (GTK_WIDGET (self->label), GTK_WIDGET (self)); } From e25e44b02a65e0c9123771cc50442b80d0ed4f32 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 14 Jun 2023 17:16:11 -0700 Subject: [PATCH 0458/1030] libsysprof-gtk: set numeric for time labels --- src/libsysprof-gtk/sysprof-time-label.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libsysprof-gtk/sysprof-time-label.c b/src/libsysprof-gtk/sysprof-time-label.c index 39ec499e..b4d71cbf 100644 --- a/src/libsysprof-gtk/sysprof-time-label.c +++ b/src/libsysprof-gtk/sysprof-time-label.c @@ -137,7 +137,10 @@ sysprof_time_label_class_init (SysprofTimeLabelClass *klass) static void sysprof_time_label_init (SysprofTimeLabel *self) { + static const char *css_classes[] = {"numeric", NULL}; + self->label = g_object_new (GTK_TYPE_LABEL, + "css-classes", css_classes, "xalign", 1.f, "single-line-mode", TRUE, NULL); From 944ed885a8c4e58c8c929c39e3e3c4a4c33727ef Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 14 Jun 2023 17:16:28 -0700 Subject: [PATCH 0459/1030] libsysprof-gtk: show column separators for mark table --- src/libsysprof-gtk/sysprof-mark-table.ui | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libsysprof-gtk/sysprof-mark-table.ui b/src/libsysprof-gtk/sysprof-mark-table.ui index 07ab3894..718d3b1e 100644 --- a/src/libsysprof-gtk/sysprof-mark-table.ui +++ b/src/libsysprof-gtk/sysprof-mark-table.ui @@ -12,6 +12,7 @@ + true Start From 344247bf8072878b16a36bb651a33e756e0482b1 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 14 Jun 2023 17:17:03 -0700 Subject: [PATCH 0460/1030] libsysprof-gtk: setup default sort for start column These may not be in order as sometimes threads record at different rates as well as marks being tacked on at the end from a memfd. --- src/libsysprof-gtk/sysprof-mark-table.c | 18 ++++++++++++++---- src/libsysprof-gtk/sysprof-mark-table.ui | 8 ++++++++ 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/libsysprof-gtk/sysprof-mark-table.c b/src/libsysprof-gtk/sysprof-mark-table.c index 09eb9bbb..7653d54e 100644 --- a/src/libsysprof-gtk/sysprof-mark-table.c +++ b/src/libsysprof-gtk/sysprof-mark-table.c @@ -32,8 +32,9 @@ struct _SysprofMarkTable SysprofSession *session; - GtkWidget *box; - GtkColumnView *column_view; + GtkWidget *box; + GtkColumnView *column_view; + GtkColumnViewColumn *start_column; }; enum { @@ -61,18 +62,26 @@ sysprof_mark_table_connect (SysprofMarkTable *self) g_autoptr(GtkSingleSelection) single = NULL; GtkFilterListModel *model; SysprofDocument *document; + GtkSorter *column_sorter; + GtkSortListModel *sort_model; g_assert (SYSPROF_IS_MARK_TABLE (self)); g_assert (SYSPROF_IS_SESSION (self->session)); + column_sorter = gtk_column_view_get_sorter (self->column_view); + document = sysprof_session_get_document (self->session); model = gtk_filter_list_model_new (sysprof_document_list_marks (document), NULL); g_object_bind_property (self->session, "filter", model, "filter", G_BINDING_SYNC_CREATE); - - single = gtk_single_selection_new (G_LIST_MODEL (g_steal_pointer (&model))); + sort_model = gtk_sort_list_model_new (G_LIST_MODEL (model), g_object_ref (column_sorter)); + single = gtk_single_selection_new (G_LIST_MODEL (sort_model)); gtk_column_view_set_model (self->column_view, GTK_SELECTION_MODEL (single)); + + gtk_column_view_sort_by_column (self->column_view, + self->start_column, + GTK_SORT_ASCENDING); } static void @@ -151,6 +160,7 @@ sysprof_mark_table_class_init (SysprofMarkTableClass *klass) gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT); gtk_widget_class_bind_template_child (widget_class, SysprofMarkTable, box); gtk_widget_class_bind_template_child (widget_class, SysprofMarkTable, column_view); + gtk_widget_class_bind_template_child (widget_class, SysprofMarkTable, start_column); g_resources_register (libsysprof_gtk_get_resource ()); diff --git a/src/libsysprof-gtk/sysprof-mark-table.ui b/src/libsysprof-gtk/sysprof-mark-table.ui index 718d3b1e..3b6df4db 100644 --- a/src/libsysprof-gtk/sysprof-mark-table.ui +++ b/src/libsysprof-gtk/sysprof-mark-table.ui @@ -16,6 +16,14 @@ Start + + + ascending + + + + + Date: Wed, 14 Jun 2023 17:21:32 -0700 Subject: [PATCH 0461/1030] libsysprof-gtk: add sorting for remaining marks columns --- src/libsysprof-gtk/sysprof-mark-table.ui | 29 ++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/libsysprof-gtk/sysprof-mark-table.ui b/src/libsysprof-gtk/sysprof-mark-table.ui index 3b6df4db..f0a5d32c 100644 --- a/src/libsysprof-gtk/sysprof-mark-table.ui +++ b/src/libsysprof-gtk/sysprof-mark-table.ui @@ -50,6 +50,14 @@ Duration + + + descending + + + + + Group + + + + + + + Type + + + + + + + Description true + + + + + + + Date: Wed, 14 Jun 2023 17:29:18 -0700 Subject: [PATCH 0462/1030] libsysprof-gtk: fix usec/msec groupings --- src/libsysprof-gtk/sysprof-time-label.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libsysprof-gtk/sysprof-time-label.c b/src/libsysprof-gtk/sysprof-time-label.c index b4d71cbf..fe2d45d9 100644 --- a/src/libsysprof-gtk/sysprof-time-label.c +++ b/src/libsysprof-gtk/sysprof-time-label.c @@ -166,9 +166,9 @@ sysprof_time_label_set_internal (SysprofTimeLabel *self, if (t == 0) str[0] = 0; - else if (t < SYSPROF_NSEC_PER_SEC/1000) + else if (t < 1000000) g_snprintf (str, sizeof str, "%.3lfμs", t/1000.); - else if (t < SYSPROF_NSEC_PER_SEC/1000000) + else if (t < SYSPROF_NSEC_PER_SEC) g_snprintf (str, sizeof str, "%.3lfms", t/1000000.); else g_snprintf (str, sizeof str, "%.3lfs", t/(double)SYSPROF_NSEC_PER_SEC); From 231535a396c6500f9a2703742e9931a1b267133d Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 14 Jun 2023 22:22:34 -0700 Subject: [PATCH 0463/1030] libsysprof-analyze: add SysprofMarkCatalog This will get used to group marks together by group->name->marks from the SysprofDocument. --- src/libsysprof-analyze/meson.build | 2 + src/libsysprof-analyze/sysprof-analyze.h | 1 + .../sysprof-mark-catalog-private.h | 32 ++++ src/libsysprof-analyze/sysprof-mark-catalog.c | 143 ++++++++++++++++++ src/libsysprof-analyze/sysprof-mark-catalog.h | 37 +++++ 5 files changed, 215 insertions(+) create mode 100644 src/libsysprof-analyze/sysprof-mark-catalog-private.h create mode 100644 src/libsysprof-analyze/sysprof-mark-catalog.c create mode 100644 src/libsysprof-analyze/sysprof-mark-catalog.h diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build index 723dc595..5c65a982 100644 --- a/src/libsysprof-analyze/meson.build +++ b/src/libsysprof-analyze/meson.build @@ -27,6 +27,7 @@ libsysprof_analyze_public_sources = [ 'sysprof-elf-symbolizer.c', 'sysprof-jitmap-symbolizer.c', 'sysprof-kallsyms-symbolizer.c', + 'sysprof-mark-catalog.c', 'sysprof-multi-symbolizer.c', 'sysprof-no-symbolizer.c', 'sysprof-symbol.c', @@ -78,6 +79,7 @@ libsysprof_analyze_public_headers = [ 'sysprof-elf-symbolizer.h', 'sysprof-jitmap-symbolizer.h', 'sysprof-kallsyms-symbolizer.h', + 'sysprof-mark-catalog.h', 'sysprof-mount.h', 'sysprof-multi-symbolizer.h', 'sysprof-no-symbolizer.h', diff --git a/src/libsysprof-analyze/sysprof-analyze.h b/src/libsysprof-analyze/sysprof-analyze.h index 7e9d2518..f89b5a2e 100644 --- a/src/libsysprof-analyze/sysprof-analyze.h +++ b/src/libsysprof-analyze/sysprof-analyze.h @@ -53,6 +53,7 @@ G_BEGIN_DECLS # include "sysprof-elf-symbolizer.h" # include "sysprof-jitmap-symbolizer.h" # include "sysprof-kallsyms-symbolizer.h" +# include "sysprof-mark-catalog.h" # include "sysprof-mount.h" # include "sysprof-multi-symbolizer.h" # include "sysprof-no-symbolizer.h" diff --git a/src/libsysprof-analyze/sysprof-mark-catalog-private.h b/src/libsysprof-analyze/sysprof-mark-catalog-private.h new file mode 100644 index 00000000..de86f1c7 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-mark-catalog-private.h @@ -0,0 +1,32 @@ +/* sysprof-mark-catalog-private.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +#include "sysprof-mark-catalog.h" + +G_BEGIN_DECLS + +SysprofMarkCatalog *_sysprof_mark_catalog_new (const char *name, + GListModel *items); + +G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-mark-catalog.c b/src/libsysprof-analyze/sysprof-mark-catalog.c new file mode 100644 index 00000000..dd7c83e7 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-mark-catalog.c @@ -0,0 +1,143 @@ +/* sysprof-mark-catalog.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-mark-catalog-private.h" + +struct _SysprofMarkCatalog +{ + GObject parent_instance; + GListModel *items; + char *name; +} SysprofMarkCatalogPrivate; + +enum { + PROP_0, + PROP_NAME, + N_PROPS +}; + +static GType +sysprof_mark_catalog_get_item_type (GListModel *model) +{ + return g_list_model_get_item_type (SYSPROF_MARK_CATALOG (model)->items); +} + +static guint +sysprof_mark_catalog_get_n_items (GListModel *model) +{ + return g_list_model_get_n_items (SYSPROF_MARK_CATALOG (model)->items); +} + +static gpointer +sysprof_mark_catalog_get_item (GListModel *model, + guint position) +{ + return g_list_model_get_item (SYSPROF_MARK_CATALOG (model)->items, position); +} + +static void +list_model_iface_init (GListModelInterface *iface) +{ + iface->get_n_items = sysprof_mark_catalog_get_n_items; + iface->get_item_type = sysprof_mark_catalog_get_item_type; + iface->get_item = sysprof_mark_catalog_get_item; +} + +G_DEFINE_FINAL_TYPE_WITH_CODE (SysprofMarkCatalog, sysprof_mark_catalog, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init)) + +static GParamSpec *properties [N_PROPS]; + +static void +sysprof_mark_catalog_dispose (GObject *object) +{ + SysprofMarkCatalog *self = (SysprofMarkCatalog *)object; + + g_clear_pointer (&self->name, g_free); + g_clear_object (&self->items); + + G_OBJECT_CLASS (sysprof_mark_catalog_parent_class)->dispose (object); +} + +static void +sysprof_mark_catalog_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofMarkCatalog *self = SYSPROF_MARK_CATALOG (object); + + switch (prop_id) + { + case PROP_NAME: + g_value_set_string (value, sysprof_mark_catalog_get_name (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_mark_catalog_class_init (SysprofMarkCatalogClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = sysprof_mark_catalog_dispose; + object_class->get_property = sysprof_mark_catalog_get_property; + + properties[PROP_NAME] = + g_param_spec_string ("name", NULL, NULL, + NULL, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); +} + +static void +sysprof_mark_catalog_init (SysprofMarkCatalog *self) +{ +} + +const char * +sysprof_mark_catalog_get_name (SysprofMarkCatalog *self) +{ + g_return_val_if_fail (SYSPROF_IS_MARK_CATALOG (self), NULL); + + return self->name; +} + +SysprofMarkCatalog * +_sysprof_mark_catalog_new (const char *name, + GListModel *items) +{ + SysprofMarkCatalog *self; + + g_return_val_if_fail (name != NULL, NULL); + g_return_val_if_fail (G_IS_LIST_MODEL (items), NULL); + + self = g_object_new (SYSPROF_TYPE_MARK_CATALOG, NULL); + self->name = g_strdup (name); + self->items = g_object_ref (items); + + return self; +} diff --git a/src/libsysprof-analyze/sysprof-mark-catalog.h b/src/libsysprof-analyze/sysprof-mark-catalog.h new file mode 100644 index 00000000..9ed89093 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-mark-catalog.h @@ -0,0 +1,37 @@ +/* sysprof-mark-catalog.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +#include + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_MARK_CATALOG (sysprof_mark_catalog_get_type()) + +SYSPROF_AVAILABLE_IN_ALL +G_DECLARE_FINAL_TYPE (SysprofMarkCatalog, sysprof_mark_catalog, SYSPROF, MARK_CATALOG, GObject) + +SYSPROF_AVAILABLE_IN_ALL +const char *sysprof_mark_catalog_get_name (SysprofMarkCatalog *self); + +G_END_DECLS From 11387e5a55e6a6a51005b0dac4378f69accf7703 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 14 Jun 2023 22:23:18 -0700 Subject: [PATCH 0464/1030] libsysprof-gtk: add scaffolding for chart widget This doesn't yet render things, but it starts to get the plumbing in place so that we can create a custom row type and then consume the marks catalog from SysprofDocument eventually. --- .../libsysprof-gtk.gresource.xml | 1 + src/libsysprof-gtk/meson.build | 2 + src/libsysprof-gtk/sysprof-gtk.h | 1 + src/libsysprof-gtk/sysprof-mark-chart.c | 216 ++++++++++++++++++ src/libsysprof-gtk/sysprof-mark-chart.h | 42 ++++ src/libsysprof-gtk/sysprof-mark-chart.ui | 73 ++++++ src/libsysprof-gtk/tests/meson.build | 1 + src/libsysprof-gtk/tests/test-mark-chart.c | 94 ++++++++ 8 files changed, 430 insertions(+) create mode 100644 src/libsysprof-gtk/sysprof-mark-chart.c create mode 100644 src/libsysprof-gtk/sysprof-mark-chart.h create mode 100644 src/libsysprof-gtk/sysprof-mark-chart.ui create mode 100644 src/libsysprof-gtk/tests/test-mark-chart.c diff --git a/src/libsysprof-gtk/libsysprof-gtk.gresource.xml b/src/libsysprof-gtk/libsysprof-gtk.gresource.xml index e8427e10..cda23497 100644 --- a/src/libsysprof-gtk/libsysprof-gtk.gresource.xml +++ b/src/libsysprof-gtk/libsysprof-gtk.gresource.xml @@ -2,6 +2,7 @@ sysprof-callgraph-view.ui + sysprof-mark-chart.ui sysprof-mark-table.ui sysprof-weighted-callgraph-view.ui style.css diff --git a/src/libsysprof-gtk/meson.build b/src/libsysprof-gtk/meson.build index 1a069bfa..6bda0e24 100644 --- a/src/libsysprof-gtk/meson.build +++ b/src/libsysprof-gtk/meson.build @@ -1,5 +1,6 @@ libsysprof_gtk_public_sources = [ 'sysprof-callgraph-view.c', + 'sysprof-mark-chart.c', 'sysprof-mark-table.c', 'sysprof-session.c', 'sysprof-weighted-callgraph-view.c', @@ -16,6 +17,7 @@ libsysprof_gtk_public_headers = [ 'sysprof-gtk.h', 'sysprof-callgraph-view.h', + 'sysprof-mark-chart.h', 'sysprof-mark-table.h', 'sysprof-session.h', 'sysprof-weighted-callgraph-view.h', diff --git a/src/libsysprof-gtk/sysprof-gtk.h b/src/libsysprof-gtk/sysprof-gtk.h index 80447058..c886f0c3 100644 --- a/src/libsysprof-gtk/sysprof-gtk.h +++ b/src/libsysprof-gtk/sysprof-gtk.h @@ -24,6 +24,7 @@ G_BEGIN_DECLS #define SYSPROF_GTK_INSIDE # include "sysprof-callgraph-view.h" +# include "sysprof-mark-chart.h" # include "sysprof-mark-table.h" # include "sysprof-session.h" # include "sysprof-weighted-callgraph-view.h" diff --git a/src/libsysprof-gtk/sysprof-mark-chart.c b/src/libsysprof-gtk/sysprof-mark-chart.c new file mode 100644 index 00000000..650253ab --- /dev/null +++ b/src/libsysprof-gtk/sysprof-mark-chart.c @@ -0,0 +1,216 @@ +/* sysprof-mark-chart.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-css-private.h" +#include "sysprof-mark-chart.h" + +#include "libsysprof-gtk-resources.h" + +struct _SysprofMarkChart +{ + GtkWidget parent_instance; + + SysprofSession *session; + + GtkWidget *box; + GtkColumnView *column_view; +}; + +enum { + PROP_0, + PROP_SESSION, + N_PROPS +}; + +G_DEFINE_FINAL_TYPE (SysprofMarkChart, sysprof_mark_chart, GTK_TYPE_WIDGET) + +static GParamSpec *properties [N_PROPS]; + +static void +sysprof_mark_chart_disconnect (SysprofMarkChart *self) +{ + g_assert (SYSPROF_IS_MARK_CHART (self)); + g_assert (SYSPROF_IS_SESSION (self->session)); + + gtk_column_view_set_model (self->column_view, NULL); +} + +static void +sysprof_mark_chart_connect (SysprofMarkChart *self) +{ +#if 0 + g_autoptr(GtkSingleSelection) single = NULL; + GtkFilterListModel *model; + SysprofDocument *document; + GtkSorter *column_sorter; + GtkSortListModel *sort_model; + + g_assert (SYSPROF_IS_MARK_CHART (self)); + g_assert (SYSPROF_IS_SESSION (self->session)); + + column_sorter = gtk_column_view_get_sorter (self->column_view); + + document = sysprof_session_get_document (self->session); + model = gtk_filter_list_model_new (sysprof_document_list_marks (document), NULL); + g_object_bind_property (self->session, "filter", model, "filter", + G_BINDING_SYNC_CREATE); + sort_model = gtk_sort_list_model_new (G_LIST_MODEL (model), g_object_ref (column_sorter)); + single = gtk_single_selection_new (G_LIST_MODEL (sort_model)); + + gtk_column_view_set_model (self->column_view, GTK_SELECTION_MODEL (single)); + + gtk_column_view_sort_by_column (self->column_view, + self->start_column, + GTK_SORT_ASCENDING); +#endif +} + +static void +sysprof_mark_chart_dispose (GObject *object) +{ + SysprofMarkChart *self = (SysprofMarkChart *)object; + + if (self->session) + { + sysprof_mark_chart_disconnect (self); + g_clear_object (&self->session); + } + + g_clear_pointer (&self->box, gtk_widget_unparent); + + G_OBJECT_CLASS (sysprof_mark_chart_parent_class)->dispose (object); +} + +static void +sysprof_mark_chart_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofMarkChart *self = SYSPROF_MARK_CHART (object); + + switch (prop_id) + { + case PROP_SESSION: + g_value_set_object (value, sysprof_mark_chart_get_session (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_mark_chart_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SysprofMarkChart *self = SYSPROF_MARK_CHART (object); + + switch (prop_id) + { + case PROP_SESSION: + sysprof_mark_chart_set_session (self, g_value_get_object (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_mark_chart_class_init (SysprofMarkChartClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->dispose = sysprof_mark_chart_dispose; + object_class->get_property = sysprof_mark_chart_get_property; + object_class->set_property = sysprof_mark_chart_set_property; + + properties [PROP_SESSION] = + g_param_spec_object ("session", NULL, NULL, + SYSPROF_TYPE_SESSION, + (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); + + gtk_widget_class_set_template_from_resource (widget_class, "/libsysprof-gtk/sysprof-mark-chart.ui"); + gtk_widget_class_set_css_name (widget_class, "markchart"); + gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT); + gtk_widget_class_bind_template_child (widget_class, SysprofMarkChart, box); + gtk_widget_class_bind_template_child (widget_class, SysprofMarkChart, column_view); + + g_resources_register (libsysprof_gtk_get_resource ()); + + g_type_ensure (SYSPROF_TYPE_DOCUMENT_MARK); +} + +static void +sysprof_mark_chart_init (SysprofMarkChart *self) +{ + _sysprof_css_init (); + + gtk_widget_init_template (GTK_WIDGET (self)); +} + +GtkWidget * +sysprof_mark_chart_new (void) +{ + return g_object_new (SYSPROF_TYPE_MARK_CHART, NULL); +} + +/** + * sysprof_mark_chart_get_session: + * @self: a #SysprofMarkChart + * + * Returns: (transfer none) (nullable): a #SysprofSession or %NULL + */ +SysprofSession * +sysprof_mark_chart_get_session (SysprofMarkChart *self) +{ + g_return_val_if_fail (SYSPROF_IS_MARK_CHART (self), NULL); + + return self->session; +} + +void +sysprof_mark_chart_set_session (SysprofMarkChart *self, + SysprofSession *session) +{ + g_return_if_fail (SYSPROF_IS_MARK_CHART (self)); + g_return_if_fail (!session || SYSPROF_IS_SESSION (session)); + + if (self->session == session) + return; + + if (self->session) + sysprof_mark_chart_disconnect (self); + + g_set_object (&self->session, session); + + if (session) + sysprof_mark_chart_connect (self); + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SESSION]); +} diff --git a/src/libsysprof-gtk/sysprof-mark-chart.h b/src/libsysprof-gtk/sysprof-mark-chart.h new file mode 100644 index 00000000..5280fb6d --- /dev/null +++ b/src/libsysprof-gtk/sysprof-mark-chart.h @@ -0,0 +1,42 @@ +/* sysprof-mark-chart.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +#include "sysprof-session.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_MARK_CHART (sysprof_mark_chart_get_type()) + +SYSPROF_AVAILABLE_IN_ALL +G_DECLARE_FINAL_TYPE (SysprofMarkChart, sysprof_mark_chart, SYSPROF, MARK_CHART, GtkWidget) + +SYSPROF_AVAILABLE_IN_ALL +GtkWidget *sysprof_mark_chart_new (void); +SYSPROF_AVAILABLE_IN_ALL +SysprofSession *sysprof_mark_chart_get_session (SysprofMarkChart *self); +SYSPROF_AVAILABLE_IN_ALL +void sysprof_mark_chart_set_session (SysprofMarkChart *self, + SysprofSession *session); + +G_END_DECLS diff --git a/src/libsysprof-gtk/sysprof-mark-chart.ui b/src/libsysprof-gtk/sysprof-mark-chart.ui new file mode 100644 index 00000000..e45fa311 --- /dev/null +++ b/src/libsysprof-gtk/sysprof-mark-chart.ui @@ -0,0 +1,73 @@ + + + + diff --git a/src/libsysprof-gtk/tests/meson.build b/src/libsysprof-gtk/tests/meson.build index f7692998..8b18bfc1 100644 --- a/src/libsysprof-gtk/tests/meson.build +++ b/src/libsysprof-gtk/tests/meson.build @@ -13,6 +13,7 @@ libsysprof_gtk_testsuite_c_args = [ libsysprof_gtk_testsuite = { 'test-callgraph' : {'skip': true}, + 'test-mark-chart' : {'skip': true}, 'test-mark-table' : {'skip': true}, } diff --git a/src/libsysprof-gtk/tests/test-mark-chart.c b/src/libsysprof-gtk/tests/test-mark-chart.c new file mode 100644 index 00000000..5cef27ff --- /dev/null +++ b/src/libsysprof-gtk/tests/test-mark-chart.c @@ -0,0 +1,94 @@ +/* test-callgraph.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include +#include +#include +#include + +static GMainLoop *main_loop; +static char *filename; +static const GOptionEntry entries[] = { + { 0 } +}; + +int +main (int argc, + char *argv[]) +{ + g_autoptr(GOptionContext) context = g_option_context_new ("- show a callgraph"); + g_autoptr(SysprofDocumentLoader) loader = NULL; + g_autoptr(SysprofDocument) document = NULL; + g_autoptr(SysprofSession) session = NULL; + g_autoptr(GError) error = NULL; + SysprofMarkChart *chart; + GtkWindow *window; + + sysprof_clock_init (); + + gtk_init (); + adw_init (); + + g_option_context_add_main_entries (context, entries, NULL); + if (!g_option_context_parse (context, &argc, &argv, &error)) + { + g_printerr ("%s\n", error->message); + return 1; + } + + if (argc != 2) + { + g_print ("usage: %s [OPTIONS] CAPTURE_FILE\n", argv[0]); + return 1; + } + + filename = argv[1]; + + main_loop = g_main_loop_new (NULL, FALSE); + + loader = sysprof_document_loader_new (filename); + sysprof_document_loader_set_symbolizer (loader, sysprof_no_symbolizer_get ()); + + g_print ("Loading %s, ignoring embedded symbols...\n", filename); + if (!(document = sysprof_document_loader_load (loader, NULL, &error))) + g_error ("Failed to load document: %s", error->message); + + session = sysprof_session_new (document); + + window = g_object_new (GTK_TYPE_WINDOW, + "default-width", 800, + "default-height", 600, + NULL); + chart = g_object_new (SYSPROF_TYPE_MARK_CHART, + "session", session, + NULL); + g_signal_connect_swapped (window, + "close-request", + G_CALLBACK (g_main_loop_quit), + main_loop); + gtk_window_set_child (window, GTK_WIDGET (chart)); + gtk_window_present (window); + + g_main_loop_run (main_loop); + + return 0; +} From b0742d1368b78e4eda042e0b72981e3d78764b8d Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 14 Jun 2023 22:31:59 -0700 Subject: [PATCH 0465/1030] libsysprof-gtk: tweak styling for marktable --- src/libsysprof-gtk/style.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libsysprof-gtk/style.css b/src/libsysprof-gtk/style.css index fcfac61b..d1abc4af 100644 --- a/src/libsysprof-gtk/style.css +++ b/src/libsysprof-gtk/style.css @@ -67,6 +67,6 @@ marktable { font-size: .9em; } -marktable row:nth-child(odd):not(:selected) { - background-color: alpha(currentColor, .05); +marktable row:nth-child(odd):not(:selected):not(:hover) { + background-color: alpha(currentColor, .02); } From 8558cd1b5828427297024d30d531e03ff141c81a Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 14 Jun 2023 22:38:42 -0700 Subject: [PATCH 0466/1030] libsysprof-gtk: remove marktable stiple This doesn't work when you have recycling rows because even/odd isn't stable. Additionally, the hover highlight is generally enough to be able to give yourself a guide to read across columns. --- src/libsysprof-gtk/style.css | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/libsysprof-gtk/style.css b/src/libsysprof-gtk/style.css index d1abc4af..121a1921 100644 --- a/src/libsysprof-gtk/style.css +++ b/src/libsysprof-gtk/style.css @@ -66,7 +66,3 @@ callgraphview row:not(:selected) treeexpander symbol.unwindable { marktable { font-size: .9em; } - -marktable row:nth-child(odd):not(:selected):not(:hover) { - background-color: alpha(currentColor, .02); -} From 51ce85b399b0a85bd75c5d84e5cb530f63cf7c68 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 15 Jun 2023 09:36:18 -0700 Subject: [PATCH 0467/1030] libsysprof-analyze: add scaffolding for listing marks by name/group --- src/libsysprof-analyze/sysprof-document.c | 18 ++++++++++++++++++ src/libsysprof-analyze/sysprof-document.h | 3 +++ 2 files changed, 21 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index 2bc3e25f..33d7eb15 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -1415,3 +1415,21 @@ sysprof_document_list_symbols_in_traceable (SysprofDocument *self, return G_LIST_MODEL (ret); } + +/** + * sysprof_document_catalog_marks: + * @self: a #SysprofDocument + * + * Generates a catalog of marks which can be used to sort marks by + * group, then another catalog by name, which is then itself a #GListModel + * of #SysprofDocumentMark. + * + * Returns: (transfer full): a #SysprofMarkCatalog + */ +SysprofMarkCatalog * +sysprof_document_catalog_marks (SysprofDocument *self) +{ + g_return_val_if_fail (SYSPROF_IS_DOCUMENT (self), NULL); + + return NULL; +} diff --git a/src/libsysprof-analyze/sysprof-document.h b/src/libsysprof-analyze/sysprof-document.h index 942f70ff..57dd50cd 100644 --- a/src/libsysprof-analyze/sysprof-document.h +++ b/src/libsysprof-analyze/sysprof-document.h @@ -27,6 +27,7 @@ #include "sysprof-callgraph.h" #include "sysprof-document-file.h" #include "sysprof-document-traceable.h" +#include "sysprof-mark-catalog.h" #include "sysprof-symbol.h" G_BEGIN_DECLS @@ -58,6 +59,8 @@ GListModel *sysprof_document_list_counters (SysprofDocument SYSPROF_AVAILABLE_IN_ALL GListModel *sysprof_document_list_marks (SysprofDocument *self); SYSPROF_AVAILABLE_IN_ALL +SysprofMarkCatalog *sysprof_document_catalog_marks (SysprofDocument *self); +SYSPROF_AVAILABLE_IN_ALL GListModel *sysprof_document_list_symbols_in_traceable (SysprofDocument *self, SysprofDocumentTraceable *traceable); SYSPROF_AVAILABLE_IN_ALL From 80dac7c0cde0c951ff79e75e7bf1f3bfa9cccade Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 15 Jun 2023 10:35:16 -0700 Subject: [PATCH 0468/1030] libsysprof-analyze: create mark catalogs from hashtable We still need to insert these when parsing the mmap'd capture on the first pass of data. --- src/libsysprof-analyze/sysprof-document.c | 46 +++++++++++++++++++++-- src/libsysprof-analyze/sysprof-document.h | 2 +- 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index 33d7eb15..35c0f8aa 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -39,6 +39,7 @@ #include "sysprof-document-overlay.h" #include "sysprof-document-process-private.h" #include "sysprof-document-symbols-private.h" +#include "sysprof-mark-catalog-private.h" #include "sysprof-mount-private.h" #include "sysprof-mount-device-private.h" #include "sysprof-mount-namespace-private.h" @@ -80,6 +81,7 @@ struct _SysprofDocument GHashTable *files_first_position; GHashTable *pid_to_process_info; GHashTable *tid_to_symbol; + GHashTable *mark_groups; SysprofMountNamespace *mount_namespace; @@ -229,6 +231,8 @@ sysprof_document_finalize (GObject *object) g_clear_pointer (&self->samples, egg_bitset_unref); g_clear_pointer (&self->traceables, egg_bitset_unref); + g_clear_pointer (&self->mark_groups, g_hash_table_unref); + g_clear_object (&self->counters); g_clear_pointer (&self->counter_id_to_values, g_hash_table_unref); @@ -274,6 +278,7 @@ sysprof_document_init (SysprofDocument *self) self->files_first_position = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); self->pid_to_process_info = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)sysprof_process_info_unref); self->tid_to_symbol = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_object_unref); + self->mark_groups = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify)g_hash_table_unref); self->mount_namespace = sysprof_mount_namespace_new (); } @@ -1424,12 +1429,47 @@ sysprof_document_list_symbols_in_traceable (SysprofDocument *self, * group, then another catalog by name, which is then itself a #GListModel * of #SysprofDocumentMark. * - * Returns: (transfer full): a #SysprofMarkCatalog + * Returns: (transfer full): a #GListModel of #SysprofMarkCatalog */ -SysprofMarkCatalog * +GListModel * sysprof_document_catalog_marks (SysprofDocument *self) { + GListStore *store; + GHashTableIter iter; + gpointer key, value; + g_return_val_if_fail (SYSPROF_IS_DOCUMENT (self), NULL); - return NULL; + store = g_list_store_new (SYSPROF_TYPE_MARK_CATALOG); + + g_hash_table_iter_init (&iter, self->mark_groups); + + while (g_hash_table_iter_next (&iter, &key, &value)) + { + g_autoptr(SysprofMarkCatalog) group = NULL; + g_autoptr(GListStore) names_store = NULL; + const char *group_name = key; + GHashTable *names = value; + GHashTableIter name_iter; + const char *name; + EggBitset *marks; + + names_store = g_list_store_new (SYSPROF_TYPE_MARK_CATALOG); + + g_hash_table_iter_init (&name_iter, names); + + while (g_hash_table_iter_next (&name_iter, (gpointer *)&name, (gpointer *)&marks)) + { + g_autoptr(GListModel) model = _sysprof_document_bitset_index_new (G_LIST_MODEL (self), marks); + g_autoptr(SysprofMarkCatalog) names_catalog = _sysprof_mark_catalog_new (name, model); + + g_list_store_append (names_store, names_catalog); + } + + group = _sysprof_mark_catalog_new (group_name, G_LIST_MODEL (names_store)); + + g_list_store_append (store, group); + } + + return G_LIST_MODEL (store); } diff --git a/src/libsysprof-analyze/sysprof-document.h b/src/libsysprof-analyze/sysprof-document.h index 57dd50cd..0d5d1329 100644 --- a/src/libsysprof-analyze/sysprof-document.h +++ b/src/libsysprof-analyze/sysprof-document.h @@ -59,7 +59,7 @@ GListModel *sysprof_document_list_counters (SysprofDocument SYSPROF_AVAILABLE_IN_ALL GListModel *sysprof_document_list_marks (SysprofDocument *self); SYSPROF_AVAILABLE_IN_ALL -SysprofMarkCatalog *sysprof_document_catalog_marks (SysprofDocument *self); +GListModel *sysprof_document_catalog_marks (SysprofDocument *self); SYSPROF_AVAILABLE_IN_ALL GListModel *sysprof_document_list_symbols_in_traceable (SysprofDocument *self, SysprofDocumentTraceable *traceable); From 8b4ab761ab62639de45a5753d2b944d54a8241e3 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 15 Jun 2023 11:44:36 -0700 Subject: [PATCH 0469/1030] libsysprof-analyze: add mark information during index pass --- src/libsysprof-analyze/sysprof-document.c | 31 +++++++ src/libsysprof-analyze/tests/meson.build | 1 + .../tests/test-mark-catalog.c | 90 +++++++++++++++++++ 3 files changed, 122 insertions(+) create mode 100644 src/libsysprof-analyze/tests/test-mark-catalog.c diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index 35c0f8aa..aab61967 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -740,6 +740,37 @@ sysprof_document_load_worker (GTask *task, g_strdup (file_chunk->path), GUINT_TO_POINTER (self->frames->len)); } + else if (tainted->type == SYSPROF_CAPTURE_FRAME_MARK) + { + const SysprofCaptureMark *mark = (const SysprofCaptureMark *)tainted; + const char *endptr = (const char *)tainted + frame_len; + + if (has_null_byte (mark->group, endptr) && + has_null_byte (mark->name, endptr)) + { + const char *group = mark->group; + const char *name = mark->name; + GHashTable *names; + EggBitset *bitset; + + if (!(names = g_hash_table_lookup (self->mark_groups, group))) + { + names = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + (GDestroyNotify)egg_bitset_unref); + g_hash_table_insert (self->mark_groups, g_strdup (group), names); + } + + if (!(bitset = g_hash_table_lookup (names, name))) + { + bitset = egg_bitset_new_empty (); + g_hash_table_insert (names, g_strdup (name), bitset); + } + + egg_bitset_add (bitset, self->frames->len); + } + } pos += frame_len; diff --git a/src/libsysprof-analyze/tests/meson.build b/src/libsysprof-analyze/tests/meson.build index 152ba494..a96a5e59 100644 --- a/src/libsysprof-analyze/tests/meson.build +++ b/src/libsysprof-analyze/tests/meson.build @@ -19,6 +19,7 @@ libsysprof_analyze_testsuite = { 'test-list-files' : {'skip': true}, 'test-list-jitmap' : {'skip': true}, 'test-list-overlays' : {'skip': true}, + 'test-mark-catalog' : {'skip': true}, 'test-print-file' : {'skip': true}, 'test-list-processes' : {'skip': true}, 'test-list-address-layout' : {'skip': true}, diff --git a/src/libsysprof-analyze/tests/test-mark-catalog.c b/src/libsysprof-analyze/tests/test-mark-catalog.c new file mode 100644 index 00000000..ed57c2e4 --- /dev/null +++ b/src/libsysprof-analyze/tests/test-mark-catalog.c @@ -0,0 +1,90 @@ +/* test-mark-catalog.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include + +#include "sysprof-document-private.h" + +int +main (int argc, + char *argv[]) +{ + g_autoptr(SysprofDocumentLoader) loader = NULL; + g_autoptr(SysprofDocument) document = NULL; + g_autoptr(GListModel) marks = NULL; + g_autoptr(GError) error = NULL; + guint n_groups; + + if (argc < 2) + { + g_printerr ("usage: %s CAPTURE_FILE\n", argv[0]); + return 1; + } + + loader = sysprof_document_loader_new (argv[1]); + sysprof_document_loader_set_symbolizer (loader, sysprof_no_symbolizer_get ()); + + if (!(document = sysprof_document_loader_load (loader, NULL, &error))) + { + g_printerr ("Failed to open capture: %s\n", error->message); + return 1; + } + + marks = sysprof_document_catalog_marks (document); + n_groups = g_list_model_get_n_items (marks); + + for (guint i = 0; i < n_groups; i++) + { + g_autoptr(SysprofMarkCatalog) catalog = g_list_model_get_item (marks, i); + const char *group = sysprof_mark_catalog_get_name (catalog); + guint n_names = g_list_model_get_n_items (G_LIST_MODEL (catalog)); + + g_assert (SYSPROF_IS_MARK_CATALOG (catalog)); + g_assert (G_IS_LIST_MODEL (catalog)); + + g_print ("%s\n", group); + + for (guint j = 0; j < n_names; j++) + { + g_autoptr(SysprofMarkCatalog) name_catalog = g_list_model_get_item (G_LIST_MODEL (catalog), j); + const char *name = sysprof_mark_catalog_get_name (name_catalog); + guint n_marks = g_list_model_get_n_items (G_LIST_MODEL (name_catalog)); + + g_assert (SYSPROF_IS_MARK_CATALOG (catalog)); + g_assert (G_IS_LIST_MODEL (catalog)); + + g_print (" %s\n", name); + + for (guint k = 0; k < n_marks; k++) + { + g_autoptr(SysprofDocumentMark) mark = g_list_model_get_item (G_LIST_MODEL (name_catalog), k); + + g_assert (SYSPROF_IS_DOCUMENT_MARK (mark)); + + g_assert_cmpstr (sysprof_document_mark_get_group (mark), ==, group); + g_assert_cmpstr (sysprof_document_mark_get_name (mark), ==, name); + + g_print (" %s\n", sysprof_document_mark_get_message (mark)); + } + } + } + + return 0; +} From 9a762fa1d4baa0a2320c2d459c66f01eb084ea47 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 15 Jun 2023 11:52:15 -0700 Subject: [PATCH 0470/1030] libsysprof-analyze: add mark catalog kind We can use this to separate catalogs for names vs groups when building tree models of them. --- src/libsysprof-analyze/sysprof-document.c | 4 +-- .../sysprof-mark-catalog-private.h | 5 ++-- src/libsysprof-analyze/sysprof-mark-catalog.c | 30 +++++++++++++++++-- src/libsysprof-analyze/sysprof-mark-catalog.h | 13 +++++++- .../tests/test-mark-catalog.c | 6 ++-- 5 files changed, 49 insertions(+), 9 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index aab61967..b236d0d6 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -1492,12 +1492,12 @@ sysprof_document_catalog_marks (SysprofDocument *self) while (g_hash_table_iter_next (&name_iter, (gpointer *)&name, (gpointer *)&marks)) { g_autoptr(GListModel) model = _sysprof_document_bitset_index_new (G_LIST_MODEL (self), marks); - g_autoptr(SysprofMarkCatalog) names_catalog = _sysprof_mark_catalog_new (name, model); + g_autoptr(SysprofMarkCatalog) names_catalog = _sysprof_mark_catalog_new (name, model, SYSPROF_MARK_CATALOG_KIND_NAME); g_list_store_append (names_store, names_catalog); } - group = _sysprof_mark_catalog_new (group_name, G_LIST_MODEL (names_store)); + group = _sysprof_mark_catalog_new (group_name, G_LIST_MODEL (names_store), SYSPROF_MARK_CATALOG_KIND_GROUP); g_list_store_append (store, group); } diff --git a/src/libsysprof-analyze/sysprof-mark-catalog-private.h b/src/libsysprof-analyze/sysprof-mark-catalog-private.h index de86f1c7..94089d4e 100644 --- a/src/libsysprof-analyze/sysprof-mark-catalog-private.h +++ b/src/libsysprof-analyze/sysprof-mark-catalog-private.h @@ -26,7 +26,8 @@ G_BEGIN_DECLS -SysprofMarkCatalog *_sysprof_mark_catalog_new (const char *name, - GListModel *items); +SysprofMarkCatalog *_sysprof_mark_catalog_new (const char *name, + GListModel *items, + SysprofMarkCatalogKind kind); G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-mark-catalog.c b/src/libsysprof-analyze/sysprof-mark-catalog.c index dd7c83e7..8f8615b8 100644 --- a/src/libsysprof-analyze/sysprof-mark-catalog.c +++ b/src/libsysprof-analyze/sysprof-mark-catalog.c @@ -27,11 +27,13 @@ struct _SysprofMarkCatalog GObject parent_instance; GListModel *items; char *name; + SysprofMarkCatalogKind kind : 1; } SysprofMarkCatalogPrivate; enum { PROP_0, PROP_NAME, + PROP_KIND, N_PROPS }; @@ -88,6 +90,10 @@ sysprof_mark_catalog_get_property (GObject *object, switch (prop_id) { + case PROP_KIND: + g_value_set_enum (value, sysprof_mark_catalog_get_kind (self)); + break; + case PROP_NAME: g_value_set_string (value, sysprof_mark_catalog_get_name (self)); break; @@ -105,6 +111,12 @@ sysprof_mark_catalog_class_init (SysprofMarkCatalogClass *klass) object_class->dispose = sysprof_mark_catalog_dispose; object_class->get_property = sysprof_mark_catalog_get_property; + properties[PROP_KIND] = + g_param_spec_enum ("kind", NULL, NULL, + SYSPROF_TYPE_MARK_CATALOG_KIND, + SYSPROF_MARK_CATALOG_KIND_GROUP, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + properties[PROP_NAME] = g_param_spec_string ("name", NULL, NULL, NULL, @@ -127,8 +139,9 @@ sysprof_mark_catalog_get_name (SysprofMarkCatalog *self) } SysprofMarkCatalog * -_sysprof_mark_catalog_new (const char *name, - GListModel *items) +_sysprof_mark_catalog_new (const char *name, + GListModel *items, + SysprofMarkCatalogKind kind) { SysprofMarkCatalog *self; @@ -138,6 +151,19 @@ _sysprof_mark_catalog_new (const char *name, self = g_object_new (SYSPROF_TYPE_MARK_CATALOG, NULL); self->name = g_strdup (name); self->items = g_object_ref (items); + self->kind = kind; return self; } + +SysprofMarkCatalogKind +sysprof_mark_catalog_get_kind (SysprofMarkCatalog *self) +{ + g_return_val_if_fail (SYSPROF_IS_MARK_CATALOG (self), 0); + + return self->kind; +} + +G_DEFINE_ENUM_TYPE (SysprofMarkCatalogKind, sysprof_mark_catalog_kind, + G_DEFINE_ENUM_VALUE (SYSPROF_MARK_CATALOG_KIND_GROUP, "group"), + G_DEFINE_ENUM_VALUE (SYSPROF_MARK_CATALOG_KIND_NAME, "name")) diff --git a/src/libsysprof-analyze/sysprof-mark-catalog.h b/src/libsysprof-analyze/sysprof-mark-catalog.h index 9ed89093..33756481 100644 --- a/src/libsysprof-analyze/sysprof-mark-catalog.h +++ b/src/libsysprof-analyze/sysprof-mark-catalog.h @@ -27,11 +27,22 @@ G_BEGIN_DECLS #define SYSPROF_TYPE_MARK_CATALOG (sysprof_mark_catalog_get_type()) +#define SYSPROF_TYPE_MARK_CATALOG_KIND (sysprof_mark_catalog_kind_get_type()) + +typedef enum _SysprofMarkCatalogKind +{ + SYSPROF_MARK_CATALOG_KIND_GROUP, + SYSPROF_MARK_CATALOG_KIND_NAME, +} SysprofMarkCatalogKind; SYSPROF_AVAILABLE_IN_ALL G_DECLARE_FINAL_TYPE (SysprofMarkCatalog, sysprof_mark_catalog, SYSPROF, MARK_CATALOG, GObject) SYSPROF_AVAILABLE_IN_ALL -const char *sysprof_mark_catalog_get_name (SysprofMarkCatalog *self); +GType sysprof_mark_catalog_kind_get_type (void) G_GNUC_CONST; +SYSPROF_AVAILABLE_IN_ALL +const char *sysprof_mark_catalog_get_name (SysprofMarkCatalog *self); +SYSPROF_AVAILABLE_IN_ALL +SysprofMarkCatalogKind sysprof_mark_catalog_get_kind (SysprofMarkCatalog *self); G_END_DECLS diff --git a/src/libsysprof-analyze/tests/test-mark-catalog.c b/src/libsysprof-analyze/tests/test-mark-catalog.c index ed57c2e4..74422e76 100644 --- a/src/libsysprof-analyze/tests/test-mark-catalog.c +++ b/src/libsysprof-analyze/tests/test-mark-catalog.c @@ -58,6 +58,7 @@ main (int argc, g_assert (SYSPROF_IS_MARK_CATALOG (catalog)); g_assert (G_IS_LIST_MODEL (catalog)); + g_assert (sysprof_mark_catalog_get_kind (catalog) == SYSPROF_MARK_CATALOG_KIND_GROUP); g_print ("%s\n", group); @@ -67,8 +68,9 @@ main (int argc, const char *name = sysprof_mark_catalog_get_name (name_catalog); guint n_marks = g_list_model_get_n_items (G_LIST_MODEL (name_catalog)); - g_assert (SYSPROF_IS_MARK_CATALOG (catalog)); - g_assert (G_IS_LIST_MODEL (catalog)); + g_assert (SYSPROF_IS_MARK_CATALOG (name_catalog)); + g_assert (G_IS_LIST_MODEL (name_catalog)); + g_assert (sysprof_mark_catalog_get_kind (name_catalog) == SYSPROF_MARK_CATALOG_KIND_NAME); g_print (" %s\n", name); From a25b7a81413a731f667a99fbbd481c597a7d4563 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 15 Jun 2023 12:34:49 -0700 Subject: [PATCH 0471/1030] libsysprof-gtk: add columnview header-factory experiment --- src/libsysprof-gtk/sysprof-mark-chart.c | 33 +++++++++++++++--------- src/libsysprof-gtk/sysprof-mark-chart.ui | 30 +++++++++++++++++++-- 2 files changed, 49 insertions(+), 14 deletions(-) diff --git a/src/libsysprof-gtk/sysprof-mark-chart.c b/src/libsysprof-gtk/sysprof-mark-chart.c index 650253ab..481923c0 100644 --- a/src/libsysprof-gtk/sysprof-mark-chart.c +++ b/src/libsysprof-gtk/sysprof-mark-chart.c @@ -45,6 +45,17 @@ G_DEFINE_FINAL_TYPE (SysprofMarkChart, sysprof_mark_chart, GTK_TYPE_WIDGET) static GParamSpec *properties [N_PROPS]; +static GListModel * +create_model_func (gpointer item, + gpointer user_data) +{ + if (SYSPROF_IS_MARK_CATALOG (item) && + sysprof_mark_catalog_get_kind (item) == SYSPROF_MARK_CATALOG_KIND_GROUP) + return g_object_ref (item); + + return NULL; +} + static void sysprof_mark_chart_disconnect (SysprofMarkChart *self) { @@ -57,12 +68,12 @@ sysprof_mark_chart_disconnect (SysprofMarkChart *self) static void sysprof_mark_chart_connect (SysprofMarkChart *self) { -#if 0 g_autoptr(GtkSingleSelection) single = NULL; - GtkFilterListModel *model; + GtkFilterListModel *filtered; + GtkTreeListModel *marks_tree; SysprofDocument *document; - GtkSorter *column_sorter; GtkSortListModel *sort_model; + GtkSorter *column_sorter; g_assert (SYSPROF_IS_MARK_CHART (self)); g_assert (SYSPROF_IS_SESSION (self->session)); @@ -70,18 +81,16 @@ sysprof_mark_chart_connect (SysprofMarkChart *self) column_sorter = gtk_column_view_get_sorter (self->column_view); document = sysprof_session_get_document (self->session); - model = gtk_filter_list_model_new (sysprof_document_list_marks (document), NULL); - g_object_bind_property (self->session, "filter", model, "filter", - G_BINDING_SYNC_CREATE); - sort_model = gtk_sort_list_model_new (G_LIST_MODEL (model), g_object_ref (column_sorter)); + marks_tree = gtk_tree_list_model_new (sysprof_document_catalog_marks (document), + FALSE, TRUE, + create_model_func, + NULL, NULL); + filtered = gtk_filter_list_model_new (G_LIST_MODEL (marks_tree), NULL); + g_object_bind_property (self->session, "filter", filtered, "filter", G_BINDING_SYNC_CREATE); + sort_model = gtk_sort_list_model_new (G_LIST_MODEL (filtered), g_object_ref (column_sorter)); single = gtk_single_selection_new (G_LIST_MODEL (sort_model)); gtk_column_view_set_model (self->column_view, GTK_SELECTION_MODEL (single)); - - gtk_column_view_sort_by_column (self->column_view, - self->start_column, - GTK_SORT_ASCENDING); -#endif } static void diff --git a/src/libsysprof-gtk/sysprof-mark-chart.ui b/src/libsysprof-gtk/sysprof-mark-chart.ui index e45fa311..42b2b079 100644 --- a/src/libsysprof-gtk/sysprof-mark-chart.ui +++ b/src/libsysprof-gtk/sysprof-mark-chart.ui @@ -12,6 +12,30 @@ + + + + + + +]]> + + + true @@ -24,8 +48,10 @@ - - GtkListItem + + + GtkListItem + From 1a806d3106bd0903c768779834174cdabdc031f1 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 15 Jun 2023 12:39:21 -0700 Subject: [PATCH 0472/1030] libsysprof-analyze: ignore empty message rows --- src/libsysprof-analyze/tests/test-mark-catalog.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libsysprof-analyze/tests/test-mark-catalog.c b/src/libsysprof-analyze/tests/test-mark-catalog.c index 74422e76..872d27b9 100644 --- a/src/libsysprof-analyze/tests/test-mark-catalog.c +++ b/src/libsysprof-analyze/tests/test-mark-catalog.c @@ -77,13 +77,15 @@ main (int argc, for (guint k = 0; k < n_marks; k++) { g_autoptr(SysprofDocumentMark) mark = g_list_model_get_item (G_LIST_MODEL (name_catalog), k); + const char *message = sysprof_document_mark_get_message (mark); g_assert (SYSPROF_IS_DOCUMENT_MARK (mark)); g_assert_cmpstr (sysprof_document_mark_get_group (mark), ==, group); g_assert_cmpstr (sysprof_document_mark_get_name (mark), ==, name); - g_print (" %s\n", sysprof_document_mark_get_message (mark)); + if (message && message[0]) + g_print (" %s\n", message); } } } From 2fb3815af3a4db4fcefcc70d785b395f2363f0a0 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 15 Jun 2023 12:45:54 -0700 Subject: [PATCH 0473/1030] libsysprof-gtk: tweak sizing and expansion --- src/libsysprof-gtk/sysprof-mark-chart.ui | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/libsysprof-gtk/sysprof-mark-chart.ui b/src/libsysprof-gtk/sysprof-mark-chart.ui index 42b2b079..6e7b3c8a 100644 --- a/src/libsysprof-gtk/sysprof-mark-chart.ui +++ b/src/libsysprof-gtk/sysprof-mark-chart.ui @@ -36,7 +36,6 @@ - true @@ -47,6 +46,9 @@ From d91e57b10d6cef6bccf83eb237bb66f180254a94 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 5 Jul 2023 15:47:52 -0700 Subject: [PATCH 0695/1030] libsysprof-gtk: use doubles for normalized values These need more precision so that we can convert them to our final position by widget width/height which may have implications with zoom. --- src/libsysprof-gtk/sysprof-column-layer.c | 10 +++--- src/libsysprof-gtk/sysprof-line-layer.c | 36 +++++++++---------- .../sysprof-normalized-series-item.c | 14 ++++---- .../sysprof-normalized-series-item.h | 2 +- .../sysprof-normalized-series.c | 22 ++++++------ .../sysprof-normalized-series.h | 4 +-- src/libsysprof-gtk/sysprof-time-span-layer.c | 35 +++++++----------- src/libsysprof-gtk/sysprof-xy-layer-private.h | 4 +-- src/libsysprof-gtk/sysprof-xy-layer.c | 4 +-- 9 files changed, 61 insertions(+), 70 deletions(-) diff --git a/src/libsysprof-gtk/sysprof-column-layer.c b/src/libsysprof-gtk/sysprof-column-layer.c index b13955d2..2fffb2e8 100644 --- a/src/libsysprof-gtk/sysprof-column-layer.c +++ b/src/libsysprof-gtk/sysprof-column-layer.c @@ -53,8 +53,8 @@ sysprof_column_layer_snapshot (GtkWidget *widget, GtkSnapshot *snapshot) { SysprofColumnLayer *self = (SysprofColumnLayer *)widget; - const float *x_values; - const float *y_values; + const double *x_values; + const double *y_values; const GdkRGBA *color; guint n_values; int width; @@ -102,7 +102,7 @@ sysprof_column_layer_snapshot (GtkWidget *widget, &GRAPHENE_RECT_INIT (x_values[i] * width, 0, 1, - ceilf (y_values[i] * height))); + ceil (y_values[i] * height))); } gtk_snapshot_restore (snapshot); @@ -115,8 +115,8 @@ sysprof_column_layer_get_index_at_coord (SysprofColumnLayer *self, graphene_rect_t *area) { graphene_point_t point; - const float *x_values; - const float *y_values; + const double *x_values; + const double *y_values; gboolean flip_y; guint best = GTK_INVALID_LIST_POSITION; guint n_values; diff --git a/src/libsysprof-gtk/sysprof-line-layer.c b/src/libsysprof-gtk/sysprof-line-layer.c index 30bb60b6..c161e669 100644 --- a/src/libsysprof-gtk/sysprof-line-layer.c +++ b/src/libsysprof-gtk/sysprof-line-layer.c @@ -72,14 +72,14 @@ sysprof_line_layer_snapshot (GtkWidget *widget, GtkSnapshot *snapshot) { SysprofLineLayer *self = (SysprofLineLayer *)widget; - const float *x_values; - const float *y_values; + const double *x_values; + const double *y_values; const GdkRGBA *color; cairo_t *cr; - float first_x; - float first_y; - float last_x; - float last_y; + double first_x; + double first_y; + double last_x; + double last_y; guint n_values; int width; int height; @@ -120,8 +120,8 @@ sysprof_line_layer_snapshot (GtkWidget *widget, { for (guint i = 1; i < n_values; i++) { - float x = floor (x_values[i] * width); - float y = floor (y_values[i] * height) + .5; + double x = floor (x_values[i] * width); + double y = floor (y_values[i] * height) + .5; /* Skip if we are getting data incorrectly on the X axis. * It should have been sorted by this point. @@ -145,8 +145,8 @@ sysprof_line_layer_snapshot (GtkWidget *widget, { for (guint i = 1; i < n_values; i++) { - float x = floor (x_values[i] * width); - float y = floor (y_values[i] * height) + .5; + double x = floor (x_values[i] * width); + double y = floor (y_values[i] * height) + .5; /* Skip if we are getting data incorrectly on the X axis. * It should have been sorted by this point. @@ -190,12 +190,12 @@ sysprof_line_layer_snapshot_motion (SysprofChartLayer *layer, { SysprofLineLayer *self = (SysprofLineLayer *)layer; const GdkRGBA *color; - const float *x_values; - const float *y_values; - float best_distance = G_MAXFLOAT; + const double *x_values; + const double *y_values; + double best_distance = G_MAXFLOAT; guint best_index = GTK_INVALID_LIST_POSITION; - float best_x = 0; - float best_y = 0; + double best_x = 0; + double best_y = 0; guint n_values; int width; int height; @@ -221,9 +221,9 @@ sysprof_line_layer_snapshot_motion (SysprofChartLayer *layer, for (guint i = 0; i < n_values; i++) { - float x2 = floor (x_values[i] * width); - float y2 = height - floor (y_values[i] * height); - float distance; + double x2 = floor (x_values[i] * width); + double y2 = height - floor (y_values[i] * height); + double distance; if (x2 + NEAR_DISTANCE < x) continue; diff --git a/src/libsysprof-gtk/sysprof-normalized-series-item.c b/src/libsysprof-gtk/sysprof-normalized-series-item.c index 829d28a0..7c666acf 100644 --- a/src/libsysprof-gtk/sysprof-normalized-series-item.c +++ b/src/libsysprof-gtk/sysprof-normalized-series-item.c @@ -26,7 +26,7 @@ struct _SysprofNormalizedSeriesItem { GObject parent_instance; GObject *item; - float value; + double value; }; enum { @@ -65,7 +65,7 @@ sysprof_normalized_series_item_get_property (GObject *object, break; case PROP_VALUE: - g_value_set_float (value, self->value); + g_value_set_double (value, self->value); break; default: @@ -88,7 +88,7 @@ sysprof_normalized_series_item_set_property (GObject *object, break; case PROP_VALUE: - self->value = g_value_get_float (value); + self->value = g_value_get_double (value); break; default: @@ -111,9 +111,9 @@ sysprof_normalized_series_item_class_init (SysprofNormalizedSeriesItemClass *kla (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); properties [PROP_VALUE] = - g_param_spec_float ("value", NULL, NULL, - 0, 1, 0, - (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + g_param_spec_double ("value", NULL, NULL, + 0, 1, 0, + (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); g_object_class_install_properties (object_class, N_PROPS, properties); } @@ -123,7 +123,7 @@ sysprof_normalized_series_item_init (SysprofNormalizedSeriesItem *self) { } -float +double sysprof_normalized_series_item_get_value (SysprofNormalizedSeriesItem *self) { g_return_val_if_fail (SYSPROF_IS_NORMALIZED_SERIES_ITEM (self), 0); diff --git a/src/libsysprof-gtk/sysprof-normalized-series-item.h b/src/libsysprof-gtk/sysprof-normalized-series-item.h index e20c8474..3d06c752 100644 --- a/src/libsysprof-gtk/sysprof-normalized-series-item.h +++ b/src/libsysprof-gtk/sysprof-normalized-series-item.h @@ -34,6 +34,6 @@ G_DECLARE_FINAL_TYPE (SysprofNormalizedSeriesItem, sysprof_normalized_series_ite SYSPROF_AVAILABLE_IN_ALL gpointer sysprof_normalized_series_item_get_item (SysprofNormalizedSeriesItem *self); SYSPROF_AVAILABLE_IN_ALL -float sysprof_normalized_series_item_get_value (SysprofNormalizedSeriesItem *self); +double sysprof_normalized_series_item_get_value (SysprofNormalizedSeriesItem *self); G_END_DECLS diff --git a/src/libsysprof-gtk/sysprof-normalized-series.c b/src/libsysprof-gtk/sysprof-normalized-series.c index f63046d6..b0ae362f 100644 --- a/src/libsysprof-gtk/sysprof-normalized-series.c +++ b/src/libsysprof-gtk/sysprof-normalized-series.c @@ -96,7 +96,7 @@ sysprof_normalized_series_update_missing (gpointer user_data) g_autoptr(GObject) item = g_list_model_get_item (model, position); g_auto(GValue) value = G_VALUE_INIT; guint next = GTK_INVALID_LIST_POSITION; - float *fval = &g_array_index (self->values, float, position); + double *fval = &g_array_index (self->values, double, position); gtk_expression_evaluate (expression, item, &value); @@ -186,12 +186,12 @@ sysprof_normalized_series_items_changed (SysprofSeries *series, } else { - static const float empty[32] = {0}; - const float *vals = empty; - float *alloc = NULL; + static const double empty[32] = {0}; + const double *vals = empty; + double *alloc = NULL; if (added > G_N_ELEMENTS (empty)) - vals = alloc = g_new0 (float, added); + vals = alloc = g_new0 (double, added); g_array_insert_vals (self->values, position, vals, added); @@ -234,7 +234,7 @@ sysprof_normalized_series_get_series_item (SysprofSeries *series, ret = g_object_new (SYSPROF_TYPE_NORMALIZED_SERIES_ITEM, "item", item, - "value", g_array_index (self->values, float, position), + "value", g_array_index (self->values, double, position), NULL); g_object_unref (item); @@ -365,7 +365,7 @@ sysprof_normalized_series_class_init (SysprofNormalizedSeriesClass *klass) static void sysprof_normalized_series_init (SysprofNormalizedSeries *self) { - self->values = g_array_new (FALSE, TRUE, sizeof (float)); + self->values = g_array_new (FALSE, TRUE, sizeof (double)); self->missing = egg_bitset_new_empty (); } @@ -404,7 +404,7 @@ sysprof_normalized_series_new (SysprofSeries *series, return SYSPROF_SERIES (normalized); } -float +double sysprof_normalized_series_get_value_at (SysprofNormalizedSeries *self, guint position) { @@ -416,7 +416,7 @@ sysprof_normalized_series_get_value_at (SysprofNormalizedSeries *self, if (position >= self->values->len) return .0; - return g_array_index (self->values, float, position); + return g_array_index (self->values, double, position); } /** @@ -534,7 +534,7 @@ sysprof_normalized_series_set_series (SysprofNormalizedSeries *self, } } -const float * +const double * sysprof_normalized_series_get_values (SysprofNormalizedSeries *self, guint *n_values) { @@ -545,7 +545,7 @@ sysprof_normalized_series_get_values (SysprofNormalizedSeries *self, *n_values = self->values->len; - return &g_array_index (self->values, float, 0); + return &g_array_index (self->values, double, 0); } void diff --git a/src/libsysprof-gtk/sysprof-normalized-series.h b/src/libsysprof-gtk/sysprof-normalized-series.h index 06b54158..6ac66378 100644 --- a/src/libsysprof-gtk/sysprof-normalized-series.h +++ b/src/libsysprof-gtk/sysprof-normalized-series.h @@ -62,10 +62,10 @@ SYSPROF_AVAILABLE_IN_ALL void sysprof_normalized_series_set_series (SysprofNormalizedSeries *self, SysprofSeries *series); SYSPROF_AVAILABLE_IN_ALL -float sysprof_normalized_series_value_at (SysprofNormalizedSeries *self, +double sysprof_normalized_series_value_at (SysprofNormalizedSeries *self, guint position); SYSPROF_AVAILABLE_IN_ALL -const float *sysprof_normalized_series_get_values (SysprofNormalizedSeries *self, +const double *sysprof_normalized_series_get_values (SysprofNormalizedSeries *self, guint *n_values); G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofNormalizedSeries, g_object_unref) diff --git a/src/libsysprof-gtk/sysprof-time-span-layer.c b/src/libsysprof-gtk/sysprof-time-span-layer.c index 897b97af..1483d4a2 100644 --- a/src/libsysprof-gtk/sysprof-time-span-layer.c +++ b/src/libsysprof-gtk/sysprof-time-span-layer.c @@ -93,13 +93,12 @@ sysprof_time_span_layer_snapshot (GtkWidget *widget, GtkSnapshot *snapshot) { SysprofTimeSpanLayer *self = (SysprofTimeSpanLayer *)widget; - const float *x_values; - const float *x2_values; + const double *x_values; + const double *x2_values; const GdkRGBA *color; const GdkRGBA *event_color; GdkRGBA mixed; graphene_rect_t box_rect; - float last_end_x = 0; guint n_x_values = 0; guint n_x2_values = 0; guint n_values; @@ -156,8 +155,8 @@ sysprof_time_span_layer_snapshot (GtkWidget *widget, */ for (guint i = 0; i < n_values; i++) { - float begin = x_values[i]; - float end = x2_values[i]; + double begin = x_values[i]; + double end = x2_values[i]; if (end < .0) continue; @@ -168,24 +167,16 @@ sysprof_time_span_layer_snapshot (GtkWidget *widget, if (begin != end) { graphene_rect_t rect; - float end_x; - rect = GRAPHENE_RECT_INIT (floorf (begin * width), + rect = GRAPHENE_RECT_INIT (floor (begin * width), 0, - ceilf ((end - begin) * width), + ceil ((end - begin) * width), height); /* Ignore empty draws */ if (rect.size.width == 0) continue; - /* Cull draw unless it will extend past last rect */ - end_x = rect.origin.x + rect.size.width; - if (end_x <= last_end_x) - continue; - else - last_end_x = end_x; - gtk_snapshot_append_color (snapshot, color, &rect); if (rect.size.width > 20) @@ -211,7 +202,7 @@ sysprof_time_span_layer_snapshot (GtkWidget *widget, if (event_color->alpha > 0) { - float last_x = -1; + double last_x = -1; box_rect = GRAPHENE_RECT_INIT (-ceil (height / 6.), -ceil (height / 6.), @@ -220,8 +211,8 @@ sysprof_time_span_layer_snapshot (GtkWidget *widget, for (guint i = 0; i < n_values; i++) { - float begin = x_values[i]; - float end = x2_values[i]; + double begin = x_values[i]; + double end = x2_values[i]; if (begin != end) continue; @@ -247,8 +238,8 @@ sysprof_time_span_layer_lookup_item (SysprofChartLayer *layer, double y) { SysprofTimeSpanLayer *self = (SysprofTimeSpanLayer *)layer; - const float *x_values; - const float *x2_values; + const double *x_values; + const double *x2_values; GListModel *model; guint n_x_values = 0; guint n_x2_values = 0; @@ -288,8 +279,8 @@ sysprof_time_span_layer_lookup_item (SysprofChartLayer *layer, /* Then match regular duration events */ for (guint i = 0; i < n_values; i++) { - float begin = x_values[i] * width; - float end = x2_values[i] * width; + double begin = x_values[i] * width; + double end = x2_values[i] * width; if (x_values[i] == x2_values[i]) continue; diff --git a/src/libsysprof-gtk/sysprof-xy-layer-private.h b/src/libsysprof-gtk/sysprof-xy-layer-private.h index d6458b70..3de387cf 100644 --- a/src/libsysprof-gtk/sysprof-xy-layer-private.h +++ b/src/libsysprof-gtk/sysprof-xy-layer-private.h @@ -47,8 +47,8 @@ struct _SysprofXYLayerClass }; void _sysprof_xy_layer_get_xy (SysprofXYLayer *self, - const float **x_values, - const float **y_values, + const double **x_values, + const double **y_values, guint *n_values); G_END_DECLS diff --git a/src/libsysprof-gtk/sysprof-xy-layer.c b/src/libsysprof-gtk/sysprof-xy-layer.c index 0c7c5a6a..3ef5b4ac 100644 --- a/src/libsysprof-gtk/sysprof-xy-layer.c +++ b/src/libsysprof-gtk/sysprof-xy-layer.c @@ -195,8 +195,8 @@ sysprof_xy_layer_init (SysprofXYLayer *self) void _sysprof_xy_layer_get_xy (SysprofXYLayer *self, - const float **x_values, - const float **y_values, + const double **x_values, + const double **y_values, guint *n_values) { guint n_x_values = 0; From af39c9611cea76af7bbc7c1073b2af3b36746d6c Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 5 Jul 2023 15:57:03 -0700 Subject: [PATCH 0696/1030] libsysprof-gtk: transfer ownership to flatten model --- src/libsysprof-gtk/sysprof-session-discover.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libsysprof-gtk/sysprof-session-discover.c b/src/libsysprof-gtk/sysprof-session-discover.c index 6ae3de70..0c2045a2 100644 --- a/src/libsysprof-gtk/sysprof-session-discover.c +++ b/src/libsysprof-gtk/sysprof-session-discover.c @@ -458,7 +458,7 @@ sysprof_session_discover_marks (SysprofSession *self, chart = g_new0 (SysprofTrackMarksChart, 1); g_set_weak_pointer (&chart->session, self); chart->document = g_object_ref (document); - chart->model = G_LIST_MODEL (gtk_flatten_list_model_new (G_LIST_MODEL (by_group))); + chart->model = G_LIST_MODEL (gtk_flatten_list_model_new (g_object_ref (G_LIST_MODEL (by_group)))); track = g_object_new (SYSPROF_TYPE_TRACK, "title", sysprof_mark_catalog_get_group (first), From 2e0f046390cf180022c11e318fe43a72fc2a7651 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 5 Jul 2023 16:07:07 -0700 Subject: [PATCH 0697/1030] libsysprof-analyze: add aggregate marks by group This is handy so that we can show aggregate results in other places without using a FlattenListModel which can mess up event ordering. --- src/libsysprof-analyze/sysprof-document.c | 39 ++++++++++++++++++++++- src/libsysprof-analyze/sysprof-document.h | 3 ++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index 512647dd..3702232a 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -1311,7 +1311,7 @@ sysprof_document_list_traceables (SysprofDocument *self) * Gets a #GListModel containing #SysprofDocumentMark found within * the #SysprofDocument. * - * Returns: (transfer full): a #GListModel of #SysprofDocumentAllocation + * Returns: (transfer full): a #GListModel of #SysprofDocumentMark */ GListModel * sysprof_document_list_marks (SysprofDocument *self) @@ -1321,6 +1321,43 @@ sysprof_document_list_marks (SysprofDocument *self) return _sysprof_document_bitset_index_new (G_LIST_MODEL (self), self->marks); } +/** + * sysprof_document_list_marks_by_group: + * @self: a #SysprofDocument + * @group: the name of the group + * + * Gets a #GListModel containing #SysprofDocumentMark found within + * the #SysprofDocument which are part of @group. + * + * Returns: (transfer full): a #GListModel of #SysprofDocumentMark + */ +GListModel * +sysprof_document_list_marks_by_group (SysprofDocument *self, + const char *group) +{ + g_autoptr(EggBitset) bitset = NULL; + GHashTable *names; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT (self), NULL); + g_return_val_if_fail (group != NULL, NULL); + + bitset = egg_bitset_new_empty (); + + if ((names = g_hash_table_lookup (self->mark_groups, group))) + { + GHashTableIter iter; + const char *name; + EggBitset *indices; + + g_hash_table_iter_init (&iter, names); + + while (g_hash_table_iter_next (&iter, (gpointer *)&name, (gpointer *)&indices)) + egg_bitset_union (bitset, indices); + } + + return _sysprof_document_bitset_index_new (G_LIST_MODEL (self), bitset); +} + /** * sysprof_document_list_allocations: * @self: a #SysprofDocument diff --git a/src/libsysprof-analyze/sysprof-document.h b/src/libsysprof-analyze/sysprof-document.h index 10e46815..b7ca962e 100644 --- a/src/libsysprof-analyze/sysprof-document.h +++ b/src/libsysprof-analyze/sysprof-document.h @@ -63,6 +63,9 @@ GListModel *sysprof_document_list_counters (SysprofDocume SYSPROF_AVAILABLE_IN_ALL GListModel *sysprof_document_list_marks (SysprofDocument *self); SYSPROF_AVAILABLE_IN_ALL +GListModel *sysprof_document_list_marks_by_group (SysprofDocument *self, + const char *group); +SYSPROF_AVAILABLE_IN_ALL GListModel *sysprof_document_catalog_marks (SysprofDocument *self); SYSPROF_AVAILABLE_IN_ALL SysprofDocumentCounter From 3722dfefd21ae5dcebab4b5c80a6afa29784b4b0 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 5 Jul 2023 16:07:35 -0700 Subject: [PATCH 0698/1030] libsysprof-gtk: use list_by_group() to get aggregate marks --- src/libsysprof-gtk/sysprof-session-discover.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/libsysprof-gtk/sysprof-session-discover.c b/src/libsysprof-gtk/sysprof-session-discover.c index 0c2045a2..7017d719 100644 --- a/src/libsysprof-gtk/sysprof-session-discover.c +++ b/src/libsysprof-gtk/sysprof-session-discover.c @@ -450,15 +450,19 @@ sysprof_session_discover_marks (SysprofSession *self, g_autoptr(SysprofMarkCatalog) first = g_list_model_get_item (by_group, 0); g_autoptr(SysprofTrack) track = NULL; SysprofTrackMarksChart *chart; - guint n_names = g_list_model_get_n_items (G_LIST_MODEL (by_group)); + const char *group; + guint n_names; if (first == NULL) continue; + n_names = g_list_model_get_n_items (G_LIST_MODEL (by_group)); + group = sysprof_mark_catalog_get_group (first); + chart = g_new0 (SysprofTrackMarksChart, 1); g_set_weak_pointer (&chart->session, self); chart->document = g_object_ref (document); - chart->model = G_LIST_MODEL (gtk_flatten_list_model_new (g_object_ref (G_LIST_MODEL (by_group)))); + chart->model = sysprof_document_list_marks_by_group (document, group); track = g_object_new (SYSPROF_TYPE_TRACK, "title", sysprof_mark_catalog_get_group (first), From 9263624fa09f77717ba966ccb119e6926dc2691f Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 5 Jul 2023 16:31:45 -0700 Subject: [PATCH 0699/1030] libsysprof-gtk: add track for samples with context switches --- src/libsysprof-analyze/sysprof-document.c | 44 ++++++++++++ src/libsysprof-analyze/sysprof-document.h | 3 + src/libsysprof-gtk/sysprof-session-discover.c | 68 +++++++++++++++---- 3 files changed, 103 insertions(+), 12 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index 3702232a..d5ecafbf 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -73,6 +73,7 @@ struct _SysprofDocument EggBitset *allocations; EggBitset *file_chunks; EggBitset *samples; + EggBitset *samples_with_context_switch; EggBitset *traceables; EggBitset *processes; EggBitset *mmaps; @@ -250,6 +251,7 @@ sysprof_document_finalize (GObject *object) g_clear_pointer (&self->pids, egg_bitset_unref); g_clear_pointer (&self->processes, egg_bitset_unref); g_clear_pointer (&self->samples, egg_bitset_unref); + g_clear_pointer (&self->samples_with_context_switch, egg_bitset_unref); g_clear_pointer (&self->traceables, egg_bitset_unref); g_clear_pointer (&self->mark_groups, g_hash_table_unref); @@ -358,6 +360,7 @@ sysprof_document_init (SysprofDocument *self) self->pids = egg_bitset_new_empty (); self->processes = egg_bitset_new_empty (); self->samples = egg_bitset_new_empty (); + self->samples_with_context_switch = egg_bitset_new_empty (); self->traceables = egg_bitset_new_empty (); self->files_first_position = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); @@ -911,6 +914,30 @@ sysprof_document_load_worker (GTask *task, g_strdup (file_chunk->path), GUINT_TO_POINTER (self->frames->len)); } + else if (tainted->type == SYSPROF_CAPTURE_FRAME_SAMPLE) + { + const SysprofCaptureSample *sample = (const SysprofCaptureSample *)tainted; + guint n_addrs = self->needs_swap ? GUINT16_SWAP_LE_BE (sample->n_addrs) : sample->n_addrs; + const guint8 *endptr = (const guint8 *)tainted + frame_len; + + /* If the sample contains a context-switch, record it */ + if ((const guint8 *)sample + (n_addrs * sizeof (SysprofAddress)) <= endptr) + { + SysprofAddressContext last_context = SYSPROF_ADDRESS_CONTEXT_USER; + + for (guint i = 0; i < n_addrs; i++) + { + SysprofAddress addr = self->needs_swap ? GUINT64_SWAP_LE_BE (sample->addrs[i]) : sample->addrs[i]; + + if (sysprof_address_is_context_switch (addr, &last_context) && + last_context == SYSPROF_ADDRESS_CONTEXT_KERNEL) + { + egg_bitset_add (self->samples_with_context_switch, self->frames->len); + break; + } + } + } + } else if (tainted->type == SYSPROF_CAPTURE_FRAME_MARK) { const SysprofCaptureMark *mark = (const SysprofCaptureMark *)tainted; @@ -1392,6 +1419,23 @@ sysprof_document_list_samples (SysprofDocument *self) return _sysprof_document_bitset_index_new (G_LIST_MODEL (self), self->samples); } +/** + * sysprof_document_list_samples_with_context_switch: + * @self: a #SysprofDocument + * + * Gets a #GListModel containing #SysprofDocumentSample found within + * the #SysprofDocument which contain a context switch. + * + * Returns: (transfer full): a #GListModel of #SysprofDocumentSample + */ +GListModel * +sysprof_document_list_samples_with_context_switch (SysprofDocument *self) +{ + g_return_val_if_fail (SYSPROF_IS_DOCUMENT (self), NULL); + + return _sysprof_document_bitset_index_new (G_LIST_MODEL (self), self->samples_with_context_switch); +} + /** * sysprof_document_list_processes: * @self: a #SysprofDocument diff --git a/src/libsysprof-analyze/sysprof-document.h b/src/libsysprof-analyze/sysprof-document.h index b7ca962e..e0d1f731 100644 --- a/src/libsysprof-analyze/sysprof-document.h +++ b/src/libsysprof-analyze/sysprof-document.h @@ -55,6 +55,9 @@ GListModel *sysprof_document_list_allocations (SysprofDocume SYSPROF_AVAILABLE_IN_ALL GListModel *sysprof_document_list_samples (SysprofDocument *self); SYSPROF_AVAILABLE_IN_ALL +GListModel *sysprof_document_list_samples_with_context_switch + (SysprofDocument *self); +SYSPROF_AVAILABLE_IN_ALL GListModel *sysprof_document_list_processes (SysprofDocument *self); SYSPROF_AVAILABLE_IN_ALL GListModel *sysprof_document_list_jitmaps (SysprofDocument *self); diff --git a/src/libsysprof-gtk/sysprof-session-discover.c b/src/libsysprof-gtk/sysprof-session-discover.c index 7017d719..7f1f3f4e 100644 --- a/src/libsysprof-gtk/sysprof-session-discover.c +++ b/src/libsysprof-gtk/sysprof-session-discover.c @@ -37,6 +37,12 @@ typedef enum _LineFlags LINE_FLAGS_NO_SPLINE = 1 << 1, } LineFlags; +typedef struct _SysprofTrackSamples +{ + SysprofSession *session; + GListModel *samples; +} SysprofTrackSamples; + typedef struct _SysprofTrackCounter { const char *track_name; @@ -99,6 +105,26 @@ static const SysprofTrackCounter discovery_counters[] = { }; +static void +sysprof_track_samples_free (SysprofTrackSamples *samples) +{ + g_clear_object (&samples->samples); + g_clear_weak_pointer (&samples->session); + g_free (samples); +} + +static SysprofTrackSamples * +sysprof_track_samples_new (SysprofSession *session, + GListModel *samples) +{ + SysprofTrackSamples *state; + + state = g_new0 (SysprofTrackSamples, 1); + state->samples = g_object_ref (samples); + g_set_weak_pointer (&state->session, session); + return state; +} + static void sysprof_track_counter_chart_free (SysprofTrackCounterChart *info) { @@ -158,24 +184,23 @@ filter_counters (GListModel *model, } static GtkWidget * -create_chart_for_samples (SysprofSession *session, - SysprofTrack *track) +create_chart_for_samples (SysprofTrack *track, + SysprofTrackSamples *state) { g_autoptr(SysprofSeries) xy_series = NULL; g_autoptr(SysprofAxis) y_axis = NULL; SysprofChartLayer *layer; - SysprofDocument *document; SysprofChart *chart; SysprofAxis *x_axis = NULL; - g_assert (SYSPROF_IS_SESSION (session)); + g_assert (state != NULL); + g_assert (SYSPROF_IS_SESSION (state->session)); g_assert (SYSPROF_IS_TRACK (track)); - document = sysprof_session_get_document (session); - x_axis = sysprof_session_get_visible_time_axis (session); + x_axis = sysprof_session_get_visible_time_axis (state->session); y_axis = sysprof_value_axis_new (0, 128); xy_series = sysprof_xy_series_new (sysprof_track_get_title (track), - sysprof_document_list_samples (document), + g_object_ref (state->samples), gtk_property_expression_new (SYSPROF_TYPE_DOCUMENT_SAMPLE, NULL, "time"), gtk_property_expression_new (SYSPROF_TYPE_DOCUMENT_SAMPLE, NULL, "stack-depth")); @@ -196,12 +221,14 @@ sysprof_session_discover_sampler (SysprofSession *self, GListStore *tracks) { g_autoptr(GListModel) samples = NULL; + g_autoptr(GListModel) samples_with_context_switch = NULL; g_assert (SYSPROF_IS_SESSION (self)); g_assert (SYSPROF_IS_DOCUMENT (document)); g_assert (G_IS_LIST_STORE (tracks)); samples = sysprof_document_list_samples (document); + samples_with_context_switch = sysprof_document_list_samples_with_context_switch (document); if (g_list_model_get_n_items (samples) > 0) { @@ -210,12 +237,29 @@ sysprof_session_discover_sampler (SysprofSession *self, track = g_object_new (SYSPROF_TYPE_TRACK, "title", _("Profiler"), NULL); - g_signal_connect_object (track, - "create-chart", - G_CALLBACK (create_chart_for_samples), - self, - G_CONNECT_SWAPPED); + g_signal_connect_data (track, + "create-chart", + G_CALLBACK (create_chart_for_samples), + sysprof_track_samples_new (self, samples), + (GClosureNotify)sysprof_track_samples_free, + 0); g_list_store_append (tracks, track); + + if (g_list_model_get_n_items (samples_with_context_switch)) + { + g_autoptr(SysprofTrack) subtrack = NULL; + + subtrack = g_object_new (SYSPROF_TYPE_TRACK, + "title", _("Context Switches"), + NULL); + g_signal_connect_data (subtrack, + "create-chart", + G_CALLBACK (create_chart_for_samples), + sysprof_track_samples_new (self, samples_with_context_switch), + (GClosureNotify)sysprof_track_samples_free, + 0); + _sysprof_track_add_subtrack (track, subtrack); + } } } From 36aa12160794aa5ec1a5a22d852e652829c0b389 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 5 Jul 2023 16:53:44 -0700 Subject: [PATCH 0700/1030] libsysprof-analyze: helper to list traceables by string search This will let you take a callgraph and create a new GListModel containing the original traceables but filtered with something like "_sysprof_*" to get all traceables containing functions starting with "_sysprof_". --- src/libsysprof-analyze/sysprof-callgraph.c | 32 ++++++++++++++++++++++ src/libsysprof-analyze/sysprof-callgraph.h | 3 ++ 2 files changed, 35 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-callgraph.c b/src/libsysprof-analyze/sysprof-callgraph.c index 555345c6..b6461dad 100644 --- a/src/libsysprof-analyze/sysprof-callgraph.c +++ b/src/libsysprof-analyze/sysprof-callgraph.c @@ -532,6 +532,38 @@ sysprof_callgraph_list_traceables_for_symbol (SysprofCallgraph *self, return G_LIST_MODEL (g_list_store_new (SYSPROF_TYPE_DOCUMENT_TRACEABLE)); } +GListModel * +sysprof_callgraph_list_traceables_for_symbols_matching (SysprofCallgraph *self, + const char *pattern) +{ + g_autoptr(GPatternSpec) pspec = NULL; + g_autoptr(EggBitset) bitset = NULL; + + g_return_val_if_fail (SYSPROF_IS_CALLGRAPH (self), NULL); + + if (pattern == NULL || pattern[0] == 0) + return g_object_ref (self->traceables); + + pspec = g_pattern_spec_new (pattern); + bitset = egg_bitset_new_empty (); + + for (guint i = 0; i < self->symbols->len; i++) + { + SysprofSymbol *symbol = g_ptr_array_index (self->symbols, i); + const char *name = sysprof_symbol_get_name (symbol); + + if (g_pattern_spec_match (pspec, strlen (name), name, NULL)) + { + SysprofCallgraphSummary *summary = g_hash_table_lookup (self->symbol_to_summary, symbol); + + if (summary != NULL) + egg_bitset_union (bitset, summary->traceables); + } + } + + return _sysprof_document_bitset_index_new (self->traceables, bitset); +} + /** * sysprof_callgraph_list_symbols: * @self: a #SysprofCallgraph diff --git a/src/libsysprof-analyze/sysprof-callgraph.h b/src/libsysprof-analyze/sysprof-callgraph.h index 2873effa..4f476d31 100644 --- a/src/libsysprof-analyze/sysprof-callgraph.h +++ b/src/libsysprof-analyze/sysprof-callgraph.h @@ -79,6 +79,9 @@ SYSPROF_AVAILABLE_IN_ALL GListModel *sysprof_callgraph_list_traceables_for_symbol (SysprofCallgraph *self, SysprofSymbol *symbol); SYSPROF_AVAILABLE_IN_ALL +GListModel *sysprof_callgraph_list_traceables_for_symbols_matching (SysprofCallgraph *self, + const char *pattern); +SYSPROF_AVAILABLE_IN_ALL void sysprof_callgraph_descendants_async (SysprofCallgraph *self, SysprofSymbol *symbol, GCancellable *cancellable, From ec593ed79c3eaa3a8a5321ed2a32fef62c097478 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 5 Jul 2023 17:05:58 -0700 Subject: [PATCH 0701/1030] libsysprof-gtk: allow selecting items in timespan layer Double clicking a time span will update the visible/selected span to match what was selected. --- src/libsysprof-gtk/sysprof-session-discover.c | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/libsysprof-gtk/sysprof-session-discover.c b/src/libsysprof-gtk/sysprof-session-discover.c index 7f1f3f4e..518daf8d 100644 --- a/src/libsysprof-gtk/sysprof-session-discover.c +++ b/src/libsysprof-gtk/sysprof-session-discover.c @@ -422,6 +422,33 @@ sysprof_session_discover_counters (SysprofSession *self, } } +static gboolean +activate_mark_cb (SysprofSession *session, + SysprofTimeSpanLayer *layer, + SysprofDocumentMark *mark, + SysprofChart *chart) +{ + gint64 duration; + + g_assert (SYSPROF_IS_SESSION (session)); + g_assert (SYSPROF_IS_TIME_SPAN_LAYER (layer)); + g_assert (SYSPROF_IS_DOCUMENT_MARK (mark)); + g_assert (SYSPROF_IS_CHART (chart)); + + if ((duration = sysprof_document_mark_get_duration (mark))) + { + gint64 t = sysprof_document_frame_get_time (SYSPROF_DOCUMENT_FRAME (mark)); + SysprofTimeSpan span = { t, t + duration }; + + sysprof_session_select_time (session, &span); + sysprof_session_zoom_to_selection (session); + + return TRUE; + } + + return FALSE; +} + static GtkWidget * create_chart_for_marks (SysprofTrack *track, SysprofTrackMarksChart *info) @@ -445,6 +472,11 @@ create_chart_for_marks (SysprofTrack *track, gtk_property_expression_new (SYSPROF_TYPE_DOCUMENT_MARK, NULL, "message")); chart = g_object_new (SYSPROF_TYPE_CHART, NULL); + g_signal_connect_object (chart, + "activate-layer-item", + G_CALLBACK (activate_mark_cb), + info->session, + G_CONNECT_SWAPPED); layer = g_object_new (SYSPROF_TYPE_TIME_SPAN_LAYER, "series", time_series, "axis", x_axis, From f41e6e89179c7441b0be5c2800fcad4ad5f7732f Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 5 Jul 2023 17:26:45 -0700 Subject: [PATCH 0702/1030] libsysprof-gtk: add color iter for graph colors --- src/libsysprof-gtk/meson.build | 1 + .../sysprof-color-iter-private.h | 37 ++++++++++ src/libsysprof-gtk/sysprof-color-iter.c | 70 +++++++++++++++++++ 3 files changed, 108 insertions(+) create mode 100644 src/libsysprof-gtk/sysprof-color-iter-private.h create mode 100644 src/libsysprof-gtk/sysprof-color-iter.c diff --git a/src/libsysprof-gtk/meson.build b/src/libsysprof-gtk/meson.build index bb6b2dc3..3b13f50b 100644 --- a/src/libsysprof-gtk/meson.build +++ b/src/libsysprof-gtk/meson.build @@ -63,6 +63,7 @@ libsysprof_gtk_public_headers = [ ] libsysprof_gtk_private_sources = [ + 'sysprof-color-iter.c', 'sysprof-css.c', 'sysprof-mark-chart-item.c', 'sysprof-mark-chart-row.c', diff --git a/src/libsysprof-gtk/sysprof-color-iter-private.h b/src/libsysprof-gtk/sysprof-color-iter-private.h new file mode 100644 index 00000000..0caae67e --- /dev/null +++ b/src/libsysprof-gtk/sysprof-color-iter-private.h @@ -0,0 +1,37 @@ +/* sysprof-color-iter-private.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +typedef struct _SysprofColorIter +{ + GdkRGBA *colors; + guint n_colors; + guint position; +} SysprofColorIter; + +void sysprof_color_iter_init (SysprofColorIter *iter); +const GdkRGBA *sysprof_color_iter_next (SysprofColorIter *iter); + +G_END_DECLS diff --git a/src/libsysprof-gtk/sysprof-color-iter.c b/src/libsysprof-gtk/sysprof-color-iter.c new file mode 100644 index 00000000..b5612a9a --- /dev/null +++ b/src/libsysprof-gtk/sysprof-color-iter.c @@ -0,0 +1,70 @@ +/* sysprof-color-iter.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-color-iter-private.h" + +static GdkRGBA *colors; +static guint n_colors; + +void +sysprof_color_iter_init (SysprofColorIter *iter) +{ + if (g_once_init_enter (&colors)) + { + static const char *color_strings[] = { + "#3584e4", + "#ff7800", + "#33d17a", + "#9141ac", + "#e5a50a", + "#63452c", + }; + GdkRGBA *_colors; + + n_colors = G_N_ELEMENTS (color_strings); + _colors = g_new0 (GdkRGBA, n_colors); + + for (guint i = 0; i < n_colors; i++) + gdk_rgba_parse (&_colors[i], color_strings[i]); + + g_once_init_leave (&colors, _colors); + } + + iter->colors = colors; + iter->n_colors = n_colors; + iter->position = 0; +} + +const GdkRGBA * +sysprof_color_iter_next (SysprofColorIter *iter) +{ + const GdkRGBA *ret; + + g_assert (iter->colors != NULL); + g_assert (iter->n_colors > 0); + + ret = &iter->colors[iter->position % iter->n_colors]; + + iter->position++; + + return ret; +} From 2810f65f1878c50200f56e7b0ae25612566c1c97 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 5 Jul 2023 17:27:16 -0700 Subject: [PATCH 0703/1030] libsysprof-gtk: use different colors for grouped counter layers This way you can see them when overlaid on the same chart (such as for CPU frequency). --- src/libsysprof-gtk/sysprof-session-discover.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/libsysprof-gtk/sysprof-session-discover.c b/src/libsysprof-gtk/sysprof-session-discover.c index 518daf8d..39aba208 100644 --- a/src/libsysprof-gtk/sysprof-session-discover.c +++ b/src/libsysprof-gtk/sysprof-session-discover.c @@ -24,6 +24,7 @@ #include "sysprof-chart.h" #include "sysprof-chart-layer.h" +#include "sysprof-color-iter-private.h" #include "sysprof-column-layer.h" #include "sysprof-line-layer.h" #include "sysprof-session-private.h" @@ -68,6 +69,7 @@ typedef struct _SysprofTrackCounterChart SysprofDocument *document; SysprofSession *session; const SysprofTrackCounter *info; + GdkRGBA color; } SysprofTrackCounterChart; typedef struct _SysprofTrackMarksChart @@ -271,6 +273,7 @@ create_chart_for_counters (SysprofTrack *track, g_autoptr(SysprofSeries) xy_series = NULL; g_autoptr(SysprofAxis) y_axis = NULL; SysprofChartLayer *layer; + SysprofColorIter colors; SysprofChart *chart; SysprofAxis *x_axis = NULL; double min_value = 0; @@ -287,6 +290,8 @@ create_chart_for_counters (SysprofTrack *track, if (!(n_items = g_list_model_get_n_items (info->counters))) return NULL; + sysprof_color_iter_init (&colors); + x_axis = sysprof_session_get_visible_time_axis (info->session); chart = g_object_new (SYSPROF_TYPE_CHART, NULL); @@ -311,6 +316,7 @@ create_chart_for_counters (SysprofTrack *track, g_autoptr(SysprofDocumentCounter) counter = g_list_model_get_item (info->counters, i); double item_min_value = sysprof_document_counter_get_min_value (counter); double item_max_value = sysprof_document_counter_get_max_value (counter); + const GdkRGBA *color; if (!ignore_range) { @@ -321,6 +327,11 @@ create_chart_for_counters (SysprofTrack *track, max_value = item_max_value; } + if (info->color.alpha > 0) + color = &info->color; + else + color = sysprof_color_iter_next (&colors); + xy_series = sysprof_xy_series_new (sysprof_track_get_title (track), g_object_ref (G_LIST_MODEL (counter)), gtk_property_expression_new (SYSPROF_TYPE_DOCUMENT_COUNTER_VALUE, NULL, "time"), @@ -329,6 +340,7 @@ create_chart_for_counters (SysprofTrack *track, layer = g_object_new (SYSPROF_TYPE_LINE_LAYER, "spline", !(info->info->flags & LINE_FLAGS_NO_SPLINE), "dashed", !!(info->info->flags & LINE_FLAGS_DASHED), + "color", color, "series", xy_series, "x-axis", x_axis, "y-axis", y_axis, @@ -365,6 +377,9 @@ sysprof_session_discover_counters (SysprofSession *self, g_autoptr(SysprofTrack) track = NULL; g_autoptr(GListModel) subtrack_counters = NULL; SysprofTrackCounterChart *chart; + SysprofColorIter iter; + + sysprof_color_iter_init (&iter); chart = g_new0 (SysprofTrackCounterChart, 1); g_set_weak_pointer (&chart->session, self); @@ -401,6 +416,7 @@ sysprof_session_discover_counters (SysprofSession *self, subchart->document = g_object_ref (document); subchart->counters = g_object_ref (G_LIST_MODEL (store)); subchart->info = info; + subchart->color = *sysprof_color_iter_next (&iter); subtrack = g_object_new (SYSPROF_TYPE_TRACK, "title", sysprof_document_counter_get_name (subtrack_counter), From d865a0ed99e571be2173d3b691c490c715cdd417 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 5 Jul 2023 17:30:16 -0700 Subject: [PATCH 0704/1030] libsysprof-gtk: add style flag for fill --- src/libsysprof-gtk/sysprof-session-discover.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/libsysprof-gtk/sysprof-session-discover.c b/src/libsysprof-gtk/sysprof-session-discover.c index 39aba208..53f4bc27 100644 --- a/src/libsysprof-gtk/sysprof-session-discover.c +++ b/src/libsysprof-gtk/sysprof-session-discover.c @@ -34,8 +34,9 @@ typedef enum _LineFlags { - LINE_FLAGS_DASHED = 1 << 0, + LINE_FLAGS_DASHED = 1 << 0, LINE_FLAGS_NO_SPLINE = 1 << 1, + LINE_FLAGS_FILL = 1 << 2, } LineFlags; typedef struct _SysprofTrackSamples @@ -85,6 +86,7 @@ static const SysprofTrackCounter discovery_counters[] = { "CPU Percent", "Combined", "CPU Percent", "Total *", .0, 100., + LINE_FLAGS_FILL, }, { @@ -95,7 +97,11 @@ static const SysprofTrackCounter discovery_counters[] = { LINE_FLAGS_DASHED, }, - { N_("Memory"), "Memory", "Used", NULL, NULL }, + { N_("Memory"), + "Memory", "Used", + NULL, NULL, + LINE_FLAGS_FILL, + }, { N_("FPS"), "gtk", "fps", "gtk", "*" }, @@ -340,6 +346,7 @@ create_chart_for_counters (SysprofTrack *track, layer = g_object_new (SYSPROF_TYPE_LINE_LAYER, "spline", !(info->info->flags & LINE_FLAGS_NO_SPLINE), "dashed", !!(info->info->flags & LINE_FLAGS_DASHED), + "fill", !!(info->info->flags & LINE_FLAGS_FILL), "color", color, "series", xy_series, "x-axis", x_axis, From 68cda701381a6a8f4a6822ab61732e3e966c9daa Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 5 Jul 2023 17:53:44 -0700 Subject: [PATCH 0705/1030] libsysprof-analyze: add format helper --- .../sysprof-document-counter-value.c | 11 +++++++++++ .../sysprof-document-counter-value.h | 12 +++++++----- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-document-counter-value.c b/src/libsysprof-analyze/sysprof-document-counter-value.c index 4fb9bf95..c374aba9 100644 --- a/src/libsysprof-analyze/sysprof-document-counter-value.c +++ b/src/libsysprof-analyze/sysprof-document-counter-value.c @@ -151,3 +151,14 @@ sysprof_document_counter_value_get_value_double (SysprofDocumentCounterValue *se else return (double)self->value.v_int64; } + +char * +sysprof_document_counter_value_format (SysprofDocumentCounterValue *self) +{ + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_COUNTER_VALUE (self), NULL); + + if (self->type == SYSPROF_CAPTURE_COUNTER_DOUBLE) + return g_strdup_printf ("%lf", self->value.v_double); + else + return g_strdup_printf ("%ld", self->value.v_int64); +} diff --git a/src/libsysprof-analyze/sysprof-document-counter-value.h b/src/libsysprof-analyze/sysprof-document-counter-value.h index d780baf8..6f7cfee4 100644 --- a/src/libsysprof-analyze/sysprof-document-counter-value.h +++ b/src/libsysprof-analyze/sysprof-document-counter-value.h @@ -32,14 +32,16 @@ SYSPROF_AVAILABLE_IN_ALL G_DECLARE_FINAL_TYPE (SysprofDocumentCounterValue, sysprof_document_counter_value, SYSPROF, DOCUMENT_COUNTER_VALUE, GObject) SYSPROF_AVAILABLE_IN_ALL -gint64 sysprof_document_counter_value_get_time (SysprofDocumentCounterValue *self); +gint64 sysprof_document_counter_value_get_time (SysprofDocumentCounterValue *self); SYSPROF_AVAILABLE_IN_ALL -void sysprof_document_counter_value_get_value (SysprofDocumentCounterValue *self, - GValue *value); +void sysprof_document_counter_value_get_value (SysprofDocumentCounterValue *self, + GValue *value); SYSPROF_AVAILABLE_IN_ALL -gint64 sysprof_document_counter_value_get_value_int64 (SysprofDocumentCounterValue *self); +gint64 sysprof_document_counter_value_get_value_int64 (SysprofDocumentCounterValue *self); SYSPROF_AVAILABLE_IN_ALL -double sysprof_document_counter_value_get_value_double (SysprofDocumentCounterValue *self); +double sysprof_document_counter_value_get_value_double (SysprofDocumentCounterValue *self); +SYSPROF_AVAILABLE_IN_ALL +char *sysprof_document_counter_value_format (SysprofDocumentCounterValue *self); G_END_DECLS From 1ac95756afa497aee85cf4f1d99ec212c52b8cdc Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 5 Jul 2023 17:54:01 -0700 Subject: [PATCH 0706/1030] libsysprof-gtk: use double math funcs and constants --- src/libsysprof-gtk/sysprof-line-layer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libsysprof-gtk/sysprof-line-layer.c b/src/libsysprof-gtk/sysprof-line-layer.c index c161e669..72291f27 100644 --- a/src/libsysprof-gtk/sysprof-line-layer.c +++ b/src/libsysprof-gtk/sysprof-line-layer.c @@ -192,7 +192,7 @@ sysprof_line_layer_snapshot_motion (SysprofChartLayer *layer, const GdkRGBA *color; const double *x_values; const double *y_values; - double best_distance = G_MAXFLOAT; + double best_distance = G_MAXDOUBLE; guint best_index = GTK_INVALID_LIST_POSITION; double best_x = 0; double best_y = 0; @@ -231,7 +231,7 @@ sysprof_line_layer_snapshot_motion (SysprofChartLayer *layer, if (x2 > x + NEAR_DISTANCE) break; - distance = sqrtf (powf (x2 - x, 2) + powf (y2 - y, 2)); + distance = sqrt (powf (x2 - x, 2) + powf (y2 - y, 2)); if (distance < best_distance) { From 3ebea4823e0d0a8a4ee3ed2e31379f1e56999b9e Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 5 Jul 2023 17:54:22 -0700 Subject: [PATCH 0707/1030] libsysprof-gtk: add lookup item support for line points --- src/libsysprof-gtk/sysprof-line-layer.c | 57 +++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/src/libsysprof-gtk/sysprof-line-layer.c b/src/libsysprof-gtk/sysprof-line-layer.c index 72291f27..36121ade 100644 --- a/src/libsysprof-gtk/sysprof-line-layer.c +++ b/src/libsysprof-gtk/sysprof-line-layer.c @@ -268,6 +268,62 @@ sysprof_line_layer_snapshot_motion (SysprofChartLayer *layer, } } +static gpointer +sysprof_line_layer_lookup_item (SysprofChartLayer *layer, + double x, + double y) +{ + SysprofLineLayer *self = (SysprofLineLayer *)layer; + SysprofXYSeries *series; + const double *x_values; + const double *y_values; + GListModel *model; + double best_distance = G_MAXDOUBLE; + guint best_index = GTK_INVALID_LIST_POSITION; + guint n_values; + int width; + int height; + + g_assert (SYSPROF_IS_LINE_LAYER (self)); + + width = gtk_widget_get_width (GTK_WIDGET (self)); + height = gtk_widget_get_height (GTK_WIDGET (self)); + + _sysprof_xy_layer_get_xy (SYSPROF_XY_LAYER (self), &x_values, &y_values, &n_values); + + if (width == 0 || height == 0 || n_values == 0) + return NULL; + + series = sysprof_xy_layer_get_series (SYSPROF_XY_LAYER (self)); + model = sysprof_series_get_model (SYSPROF_SERIES (series)); + + for (guint i = 0; i < n_values; i++) + { + double x2 = floor (x_values[i] * width); + double y2 = height - floor (y_values[i] * height); + double distance; + + if (x2 + NEAR_DISTANCE < x) + continue; + + if (x2 > x + NEAR_DISTANCE) + break; + + distance = sqrt (pow (x2 - x, 2) + pow (y2 - y, 2)); + + if (distance < best_distance) + { + best_distance = distance; + best_index = i; + } + } + + if (best_index != GTK_INVALID_LIST_POSITION) + return g_list_model_get_item (model, best_index); + + return NULL; +} + static void sysprof_line_layer_get_property (GObject *object, guint prop_id, @@ -351,6 +407,7 @@ sysprof_line_layer_class_init (SysprofLineLayerClass *klass) widget_class->snapshot = sysprof_line_layer_snapshot; chart_layer_class->snapshot_motion = sysprof_line_layer_snapshot_motion; + chart_layer_class->lookup_item = sysprof_line_layer_lookup_item; properties [PROP_ANTIALIAS] = g_param_spec_boolean ("antialias", NULL, NULL, From ccb7bb4f27de7fdbeac71c6c26e575e1d1f2b590 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 5 Jul 2023 17:54:35 -0700 Subject: [PATCH 0708/1030] libsysprof-gtk: tweak fps line drawing --- src/libsysprof-gtk/sysprof-session-discover.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/libsysprof-gtk/sysprof-session-discover.c b/src/libsysprof-gtk/sysprof-session-discover.c index 53f4bc27..9d6f38b6 100644 --- a/src/libsysprof-gtk/sysprof-session-discover.c +++ b/src/libsysprof-gtk/sysprof-session-discover.c @@ -34,9 +34,10 @@ typedef enum _LineFlags { - LINE_FLAGS_DASHED = 1 << 0, - LINE_FLAGS_NO_SPLINE = 1 << 1, - LINE_FLAGS_FILL = 1 << 2, + LINE_FLAGS_DASHED = 1 << 0, + LINE_FLAGS_NO_SPLINE = 1 << 1, + LINE_FLAGS_FILL = 1 << 2, + LINE_FLAGS_IGNORE_RANGE_ON_SUBCHART = 1 << 3, } LineFlags; typedef struct _SysprofTrackSamples @@ -71,6 +72,7 @@ typedef struct _SysprofTrackCounterChart SysprofSession *session; const SysprofTrackCounter *info; GdkRGBA color; + guint apply_range : 1; } SysprofTrackCounterChart; typedef struct _SysprofTrackMarksChart @@ -103,7 +105,8 @@ static const SysprofTrackCounter discovery_counters[] = { LINE_FLAGS_FILL, }, - { N_("FPS"), "gtk", "fps", "gtk", "*" }, + { N_("FPS"), "gtk", "fps", "gtk", "*", + 0, 144, LINE_FLAGS_IGNORE_RANGE_ON_SUBCHART }, { N_("Energy"), @@ -310,7 +313,8 @@ create_chart_for_counters (SysprofTrack *track, max_value = sysprof_document_counter_get_max_value (first); y_axis = sysprof_value_axis_new (min_value, max_value); - if (info->info->min_value != .0 || info->info->max_value != .0) + if (info->apply_range && + (info->info->min_value != .0 || info->info->max_value != .0)) { min_value = info->info->min_value; max_value = info->info->max_value; @@ -393,6 +397,7 @@ sysprof_session_discover_counters (SysprofSession *self, chart->document = g_object_ref (document); chart->counters = g_object_ref (track_counters); chart->info = info; + chart->apply_range = TRUE; track = g_object_new (SYSPROF_TYPE_TRACK, "title", info->track_name, @@ -423,6 +428,7 @@ sysprof_session_discover_counters (SysprofSession *self, subchart->document = g_object_ref (document); subchart->counters = g_object_ref (G_LIST_MODEL (store)); subchart->info = info; + subchart->apply_range = !(info->flags & LINE_FLAGS_IGNORE_RANGE_ON_SUBCHART); subchart->color = *sysprof_color_iter_next (&iter); subtrack = g_object_new (SYSPROF_TYPE_TRACK, From 6e13590f6249239bdc3ffa07a26c76579b690ba0 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 5 Jul 2023 17:54:46 -0700 Subject: [PATCH 0709/1030] libsysprof-gtk: show counter values in informative --- src/libsysprof-gtk/sysprof-session.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/libsysprof-gtk/sysprof-session.c b/src/libsysprof-gtk/sysprof-session.c index bcf83054..8238f3f4 100644 --- a/src/libsysprof-gtk/sysprof-session.c +++ b/src/libsysprof-gtk/sysprof-session.c @@ -451,5 +451,22 @@ _sysprof_session_describe (SysprofSession *self, return g_string_free (str, FALSE); } + if (SYSPROF_IS_DOCUMENT_COUNTER_VALUE (item)) + { + SysprofDocumentCounterValue *value = item; + g_autofree char *value_str = sysprof_document_counter_value_format (value); + const SysprofTimeSpan *begin = sysprof_document_get_time_span (self->document); + gint64 t = sysprof_document_counter_value_get_time (value); + GString *str = g_string_new (NULL); + SysprofTimeSpan span = { t, t }; + + span = sysprof_time_span_relative_to (span, begin->begin_nsec); + + append_time_string (str, &span); + g_string_append_printf (str, ": %s", value_str); + + return g_string_free (str, FALSE); + } + return NULL; } From ec77e2b2ff4750bcf9beea1cd3b8f166da883a76 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 5 Jul 2023 18:02:09 -0700 Subject: [PATCH 0710/1030] libsysprof-gtk: add more colors to color iter --- src/libsysprof-gtk/sysprof-color-iter.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/libsysprof-gtk/sysprof-color-iter.c b/src/libsysprof-gtk/sysprof-color-iter.c index b5612a9a..051184a1 100644 --- a/src/libsysprof-gtk/sysprof-color-iter.c +++ b/src/libsysprof-gtk/sysprof-color-iter.c @@ -37,6 +37,14 @@ sysprof_color_iter_init (SysprofColorIter *iter) "#9141ac", "#e5a50a", "#63452c", + "#ffbe6f" + "#613583", + "#9a9996", + "#ed333b", + "#1a5fb4", + "#26a269", + "#c061cb", + "#c64600", }; GdkRGBA *_colors; From 7d93b9f581d5e804503367b2949999287467c1da Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 5 Jul 2023 18:06:42 -0700 Subject: [PATCH 0711/1030] libsysprof-analyze: add stack traces w/o kernel context This restores the functionality we had previously for user vs kernel. --- src/libsysprof-analyze/sysprof-document.c | 22 +++++++++++++++++++ src/libsysprof-analyze/sysprof-document.h | 3 +++ src/libsysprof-gtk/sysprof-session-discover.c | 20 ++++++++++++++++- 3 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index d5ecafbf..5ec368ae 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -1436,6 +1436,28 @@ sysprof_document_list_samples_with_context_switch (SysprofDocument *self) return _sysprof_document_bitset_index_new (G_LIST_MODEL (self), self->samples_with_context_switch); } +/** + * sysprof_document_list_samples_without_context_switch: + * @self: a #SysprofDocument + * + * Gets a #GListModel containing #SysprofDocumentSample found within + * the #SysprofDocument which contain a context switch. + * + * Returns: (transfer full): a #GListModel of #SysprofDocumentSample + */ +GListModel * +sysprof_document_list_samples_without_context_switch (SysprofDocument *self) +{ + g_autoptr(EggBitset) bitset = NULL; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT (self), NULL); + + bitset = egg_bitset_copy (self->samples); + egg_bitset_subtract (bitset, self->samples_with_context_switch); + + return _sysprof_document_bitset_index_new (G_LIST_MODEL (self), bitset); +} + /** * sysprof_document_list_processes: * @self: a #SysprofDocument diff --git a/src/libsysprof-analyze/sysprof-document.h b/src/libsysprof-analyze/sysprof-document.h index e0d1f731..233a0d5a 100644 --- a/src/libsysprof-analyze/sysprof-document.h +++ b/src/libsysprof-analyze/sysprof-document.h @@ -58,6 +58,9 @@ SYSPROF_AVAILABLE_IN_ALL GListModel *sysprof_document_list_samples_with_context_switch (SysprofDocument *self); SYSPROF_AVAILABLE_IN_ALL +GListModel *sysprof_document_list_samples_without_context_switch + (SysprofDocument *self); +SYSPROF_AVAILABLE_IN_ALL GListModel *sysprof_document_list_processes (SysprofDocument *self); SYSPROF_AVAILABLE_IN_ALL GListModel *sysprof_document_list_jitmaps (SysprofDocument *self); diff --git a/src/libsysprof-gtk/sysprof-session-discover.c b/src/libsysprof-gtk/sysprof-session-discover.c index 9d6f38b6..bfa92809 100644 --- a/src/libsysprof-gtk/sysprof-session-discover.c +++ b/src/libsysprof-gtk/sysprof-session-discover.c @@ -233,6 +233,7 @@ sysprof_session_discover_sampler (SysprofSession *self, { g_autoptr(GListModel) samples = NULL; g_autoptr(GListModel) samples_with_context_switch = NULL; + g_autoptr(GListModel) samples_without_context_switch = NULL; g_assert (SYSPROF_IS_SESSION (self)); g_assert (SYSPROF_IS_DOCUMENT (document)); @@ -240,6 +241,7 @@ sysprof_session_discover_sampler (SysprofSession *self, samples = sysprof_document_list_samples (document); samples_with_context_switch = sysprof_document_list_samples_with_context_switch (document); + samples_without_context_switch = sysprof_document_list_samples_without_context_switch (document); if (g_list_model_get_n_items (samples) > 0) { @@ -261,7 +263,7 @@ sysprof_session_discover_sampler (SysprofSession *self, g_autoptr(SysprofTrack) subtrack = NULL; subtrack = g_object_new (SYSPROF_TYPE_TRACK, - "title", _("Context Switches"), + "title", _("Stack Traces (In Kernel)"), NULL); g_signal_connect_data (subtrack, "create-chart", @@ -271,6 +273,22 @@ sysprof_session_discover_sampler (SysprofSession *self, 0); _sysprof_track_add_subtrack (track, subtrack); } + + if (g_list_model_get_n_items (samples_without_context_switch)) + { + g_autoptr(SysprofTrack) subtrack = NULL; + + subtrack = g_object_new (SYSPROF_TYPE_TRACK, + "title", _("Stack Traces (In User)"), + NULL); + g_signal_connect_data (subtrack, + "create-chart", + G_CALLBACK (create_chart_for_samples), + sysprof_track_samples_new (self, samples_without_context_switch), + (GClosureNotify)sysprof_track_samples_free, + 0); + _sysprof_track_add_subtrack (track, subtrack); + } } } From 9fd20c306f3027882a03826d89015d9551ea32d5 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 6 Jul 2023 10:18:52 -0700 Subject: [PATCH 0712/1030] libsysprof-analyze: warn on non-aligned or short-frames And bail doing any further processing on the capture. --- src/libsysprof-analyze/sysprof-document.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index 5ec368ae..36844342 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -841,7 +841,17 @@ sysprof_document_load_worker (GTask *task, frame_len = GUINT16_SWAP_LE_BE (frame_len); if (frame_len < sizeof (SysprofCaptureFrame)) - break; + { + g_warning ("Capture contained implausibly short frame"); + break; + } + + if (frame_len % SYSPROF_CAPTURE_ALIGN != 0) + { + g_warning ("Capture contained frame not aligned to %u", + (guint)SYSPROF_CAPTURE_ALIGN); + break; + } ptr.offset = pos; ptr.length = frame_len; From 7f23fd5e9e30e9f2711149c83c9adfd5a32288bb Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 6 Jul 2023 10:19:55 -0700 Subject: [PATCH 0713/1030] libsysprof-analyze: pre-sort frames during loading That way we can be sure that we only have to look forward to find closing pairs of operation (such as allocation/free, or appended capture data from controlfd ring buffers). --- src/libsysprof-analyze/sysprof-document.c | 149 +++++++++++++++++----- 1 file changed, 116 insertions(+), 33 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index 36844342..3f62837f 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -774,6 +774,80 @@ load_progress (Load *load, load->progress (fraction, message, load->progress_data); } +static int +sort_by_time (gconstpointer a, + gconstpointer b, + gpointer base) +{ + const SysprofDocumentFramePointer *aptr = a; + const SysprofDocumentFramePointer *bptr = b; + const SysprofCaptureFrame *aframe = (const SysprofCaptureFrame *)((const guint8 *)base + aptr->offset); + const SysprofCaptureFrame *bframe = (const SysprofCaptureFrame *)((const guint8 *)base + bptr->offset); + + if (aframe->time < bframe->time) + return -1; + + if (aframe->time > bframe->time) + return 1; + + if (aframe->type == SYSPROF_CAPTURE_FRAME_MARK && bframe->type == SYSPROF_CAPTURE_FRAME_MARK) + { + const SysprofCaptureMark *amark = (const SysprofCaptureMark *)aframe; + const SysprofCaptureMark *bmark = (const SysprofCaptureMark *)bframe; + + if (amark->duration > bmark->duration) + return -1; + else if (amark->duration < bmark->duration) + return 1; + } + + return 0; +} + +static int +sort_by_time_swapped (gconstpointer a, + gconstpointer b, + gpointer base) +{ + const SysprofDocumentFramePointer *aptr = a; + const SysprofDocumentFramePointer *bptr = b; + const SysprofCaptureFrame *aframe = (const SysprofCaptureFrame *)((const guint8 *)base + aptr->offset); + const SysprofCaptureFrame *bframe = (const SysprofCaptureFrame *)((const guint8 *)base + bptr->offset); +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + gint64 atime = GINT64_FROM_BE (aframe->time); + gint64 btime = GINT64_FROM_BE (bframe->time); +#else + gint64 atime = GINT64_FROM_LE (aframe->time); + gint64 btime = GINT64_FROM_LE (bframe->time); +#endif + + if (atime < btime) + return -1; + + if (atime > btime) + return 1; + + if (aframe->type == SYSPROF_CAPTURE_FRAME_MARK && bframe->type == SYSPROF_CAPTURE_FRAME_MARK) + { + const SysprofCaptureMark *amark = (const SysprofCaptureMark *)aframe; + const SysprofCaptureMark *bmark = (const SysprofCaptureMark *)bframe; +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + gint64 aduration = GINT64_FROM_BE (amark->duration); + gint64 bduration = GINT64_FROM_BE (bmark->duration); +#else + gint64 aduration = GINT64_FROM_LE (amark->duration); + gint64 bduration = GINT64_FROM_LE (bmark->duration); +#endif + + if (aduration > bduration) + return -1; + else if (aduration < bduration) + return 1; + } + + return 0; +} + static void sysprof_document_load_worker (GTask *task, gpointer source_object, @@ -830,11 +904,8 @@ sysprof_document_load_worker (GTask *task, pos = sizeof self->header; while (pos < (len - sizeof(guint16))) { - const SysprofCaptureFrame *tainted; SysprofDocumentFramePointer ptr; - gint64 t; guint16 frame_len; - int pid; memcpy (&frame_len, &self->base[pos], sizeof frame_len); if (self->needs_swap) @@ -856,10 +927,32 @@ sysprof_document_load_worker (GTask *task, ptr.offset = pos; ptr.length = frame_len; - tainted = (const SysprofCaptureFrame *)(gpointer)&self->base[pos]; + pos += frame_len; + count++; - pid = self->needs_swap ? GUINT32_SWAP_LE_BE (tainted->pid) : tainted->pid; - t = self->needs_swap ? GUINT64_SWAP_LE_BE (tainted->time) : tainted->time; + g_array_append_val (self->frames, ptr); + + if (count % 100 == 0) + load_progress (load, + (pos / (double)len) * .4, + _("Indexing capture data frames")); + } + + /* Now sort all the items by their respective frame times as frames + * cat into the writer may be tacked on at the end even though they + * have state which belongs earlier in the capture. + */ + if G_UNLIKELY (self->needs_swap) + g_array_sort_with_data (self->frames, sort_by_time_swapped, (gpointer)self->base); + else + g_array_sort_with_data (self->frames, sort_by_time, (gpointer)self->base); + + for (guint f = 0; f < self->frames->len; f++) + { + SysprofDocumentFramePointer *ptr = &g_array_index (self->frames, SysprofDocumentFramePointer, f); + const SysprofCaptureFrame *tainted = (const SysprofCaptureFrame *)(gpointer)&self->base[ptr->offset]; + gint64 t = self->needs_swap ? GUINT64_SWAP_LE_BE (tainted->time) : tainted->time; + int pid = self->needs_swap ? GUINT32_SWAP_LE_BE (tainted->pid) : tainted->pid; if (t > guessed_end_nsec && is_data_type (tainted->type)) guessed_end_nsec = t; @@ -869,45 +962,45 @@ sysprof_document_load_worker (GTask *task, switch ((int)tainted->type) { case SYSPROF_CAPTURE_FRAME_ALLOCATION: - egg_bitset_add (self->allocations, self->frames->len); - egg_bitset_add (self->traceables, self->frames->len); + egg_bitset_add (self->allocations, f); + egg_bitset_add (self->traceables, f); break; case SYSPROF_CAPTURE_FRAME_SAMPLE: - egg_bitset_add (self->samples, self->frames->len); - egg_bitset_add (self->traceables, self->frames->len); + egg_bitset_add (self->samples, f); + egg_bitset_add (self->traceables, f); break; case SYSPROF_CAPTURE_FRAME_PROCESS: - egg_bitset_add (self->processes, self->frames->len); + egg_bitset_add (self->processes, f); break; case SYSPROF_CAPTURE_FRAME_FILE_CHUNK: - egg_bitset_add (self->file_chunks, self->frames->len); + egg_bitset_add (self->file_chunks, f); break; case SYSPROF_CAPTURE_FRAME_CTRDEF: - egg_bitset_add (self->ctrdefs, self->frames->len); + egg_bitset_add (self->ctrdefs, f); break; case SYSPROF_CAPTURE_FRAME_CTRSET: - egg_bitset_add (self->ctrsets, self->frames->len); + egg_bitset_add (self->ctrsets, f); break; case SYSPROF_CAPTURE_FRAME_MARK: - egg_bitset_add (self->marks, self->frames->len); + egg_bitset_add (self->marks, f); break; case SYSPROF_CAPTURE_FRAME_JITMAP: - egg_bitset_add (self->jitmaps, self->frames->len); + egg_bitset_add (self->jitmaps, f); break; case SYSPROF_CAPTURE_FRAME_MAP: - egg_bitset_add (self->mmaps, self->frames->len); + egg_bitset_add (self->mmaps, f); break; case SYSPROF_CAPTURE_FRAME_OVERLAY: - egg_bitset_add (self->overlays, self->frames->len); + egg_bitset_add (self->overlays, f); break; default: @@ -922,13 +1015,13 @@ sysprof_document_load_worker (GTask *task, !g_hash_table_contains (self->files_first_position, file_chunk->path)) g_hash_table_insert (self->files_first_position, g_strdup (file_chunk->path), - GUINT_TO_POINTER (self->frames->len)); + GUINT_TO_POINTER (f)); } else if (tainted->type == SYSPROF_CAPTURE_FRAME_SAMPLE) { const SysprofCaptureSample *sample = (const SysprofCaptureSample *)tainted; guint n_addrs = self->needs_swap ? GUINT16_SWAP_LE_BE (sample->n_addrs) : sample->n_addrs; - const guint8 *endptr = (const guint8 *)tainted + frame_len; + const guint8 *endptr = (const guint8 *)tainted + ptr->length; /* If the sample contains a context-switch, record it */ if ((const guint8 *)sample + (n_addrs * sizeof (SysprofAddress)) <= endptr) @@ -942,7 +1035,7 @@ sysprof_document_load_worker (GTask *task, if (sysprof_address_is_context_switch (addr, &last_context) && last_context == SYSPROF_ADDRESS_CONTEXT_KERNEL) { - egg_bitset_add (self->samples_with_context_switch, self->frames->len); + egg_bitset_add (self->samples_with_context_switch, f); break; } } @@ -951,7 +1044,7 @@ sysprof_document_load_worker (GTask *task, else if (tainted->type == SYSPROF_CAPTURE_FRAME_MARK) { const SysprofCaptureMark *mark = (const SysprofCaptureMark *)tainted; - const char *endptr = (const char *)tainted + frame_len; + const char *endptr = (const char *)tainted + ptr->length; gint64 duration = self->needs_swap ? GUINT64_SWAP_LE_BE (mark->duration) : mark->duration; if (t + duration > guessed_end_nsec) @@ -980,19 +1073,9 @@ sysprof_document_load_worker (GTask *task, g_hash_table_insert (names, g_strdup (name), bitset); } - egg_bitset_add (bitset, self->frames->len); + egg_bitset_add (bitset, f); } } - - pos += frame_len; - count++; - - g_array_append_val (self->frames, ptr); - - if (count % 100 == 0) - load_progress (load, - (pos / (double)len) * .4, - _("Indexing capture data frames")); } if (guessed_end_nsec != 0) From 4793b12ca2c54fa77fab8a282359e881404cd505 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 6 Jul 2023 10:53:03 -0700 Subject: [PATCH 0714/1030] libsysprof-profile: use GByteArray for read buffer That way we aren't relying on the stack for access to buffer data. --- src/libsysprof-profile/sysprof-disk-usage.c | 9 ++++++--- src/libsysprof-profile/sysprof-memory-usage.c | 11 +++++++---- src/libsysprof-profile/sysprof-network-usage.c | 12 +++++++----- 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/libsysprof-profile/sysprof-disk-usage.c b/src/libsysprof-profile/sysprof-disk-usage.c index 918689f3..bd66a984 100644 --- a/src/libsysprof-profile/sysprof-disk-usage.c +++ b/src/libsysprof-profile/sysprof-disk-usage.c @@ -180,10 +180,10 @@ find_device_by_name (Record *record, static DexFuture * sysprof_disk_usage_record_fiber (gpointer user_data) { + g_autoptr(GByteArray) buf = NULL; Record *record = user_data; SysprofCaptureWriter *writer; g_autofd int stat_fd = -1; - char buf[4096*4]; LineReader reader; DiskUsage *combined; gint64 combined_reads_total = 0; @@ -194,6 +194,9 @@ sysprof_disk_usage_record_fiber (gpointer user_data) g_assert (SYSPROF_IS_RECORDING (record->recording)); g_assert (DEX_IS_CANCELLABLE (record->cancellable)); + buf = g_byte_array_new (); + g_byte_array_set_size (buf, 4096*4); + if (-1 == (stat_fd = open ("/proc/diskstats", O_RDONLY|O_CLOEXEC))) return dex_future_new_for_errno (errno); @@ -212,7 +215,7 @@ sysprof_disk_usage_record_fiber (gpointer user_data) * recording loop. If cancellation future rejects, then * we also break out of our recording loop. */ - read_future = dex_aio_read (NULL, stat_fd, buf, sizeof buf-1, 0); + read_future = dex_aio_read (NULL, stat_fd, buf->data, buf->len-1, 0); if (!dex_await (dex_future_first (dex_ref (record->cancellable), dex_ref (read_future), NULL), @@ -223,7 +226,7 @@ sysprof_disk_usage_record_fiber (gpointer user_data) if (n_read < 0) break; - line_reader_init (&reader, buf, n_read); + line_reader_init (&reader, (char *)buf->data, n_read); while ((line = line_reader_next (&reader, &line_len))) { DiskUsage ds = {0}; diff --git a/src/libsysprof-profile/sysprof-memory-usage.c b/src/libsysprof-profile/sysprof-memory-usage.c index ee543025..a4172643 100644 --- a/src/libsysprof-profile/sysprof-memory-usage.c +++ b/src/libsysprof-profile/sysprof-memory-usage.c @@ -171,18 +171,21 @@ record_free (gpointer data) static DexFuture * sysprof_memory_usage_record_fiber (gpointer user_data) { + g_autoptr(GByteArray) buf = NULL; Record *record = user_data; SysprofCaptureWriter *writer; g_autoptr(GError) error = NULL; SysprofCaptureCounter counters[1]; MemStat st; - char buf[4096]; guint counter_id; g_assert (record != NULL); g_assert (SYSPROF_IS_RECORDING (record->recording)); g_assert (DEX_IS_FUTURE (record->cancellable)); + buf = g_byte_array_new (); + g_byte_array_set_size (buf, 4096); + writer = _sysprof_recording_writer (record->recording); if (!mem_stat_open (&st, &error)) @@ -207,7 +210,7 @@ sysprof_memory_usage_record_fiber (gpointer user_data) for (;;) { - g_autoptr(DexFuture) read_future = dex_aio_read (NULL, st.stat_fd, buf, sizeof buf-1, 0); + g_autoptr(DexFuture) read_future = dex_aio_read (NULL, st.stat_fd, buf->data, buf->len-1, 0); gssize n_read; dex_await (dex_future_first (dex_ref (read_future), @@ -224,9 +227,9 @@ sysprof_memory_usage_record_fiber (gpointer user_data) if (n_read > 0) { - buf[n_read] = 0; + buf->data[n_read] = 0; - mem_stat_parse (&st, buf); + mem_stat_parse (&st, (char *)buf->data); sysprof_capture_writer_set_counters (writer, SYSPROF_CAPTURE_CURRENT_TIME, diff --git a/src/libsysprof-profile/sysprof-network-usage.c b/src/libsysprof-profile/sysprof-network-usage.c index 4a4160f2..e6d5d85a 100644 --- a/src/libsysprof-profile/sysprof-network-usage.c +++ b/src/libsysprof-profile/sysprof-network-usage.c @@ -107,7 +107,7 @@ find_device_by_name (GArray *ar, static DexFuture * sysprof_network_usage_record_fiber (gpointer user_data) { - char buf[4096*2]; + g_autoptr(GByteArray) buf = g_byte_array_new (); Record *record = user_data; g_autofree SysprofCaptureCounterValue *values = NULL; g_autofree guint *ids = NULL; @@ -128,6 +128,8 @@ sysprof_network_usage_record_fiber (gpointer user_data) g_assert (SYSPROF_IS_RECORDING (record->recording)); g_assert (DEX_IS_FUTURE (record->cancellable)); + g_byte_array_set_size (buf, 4096*2); + writer = _sysprof_recording_writer (record->recording); if (-1 == (stat_fd = open ("/proc/net/dev", O_RDONLY|O_CLOEXEC))) @@ -158,12 +160,12 @@ sysprof_network_usage_record_fiber (gpointer user_data) -1, ctr, G_N_ELEMENTS (ctr)); - n_read = dex_await_int64 (dex_aio_read (NULL, stat_fd, buf, sizeof buf, 0), &error); + n_read = dex_await_int64 (dex_aio_read (NULL, stat_fd, buf->data, buf->len, 0), &error); if (n_read <= 0) return dex_future_new_for_errno (errno); lineno = 0; - line_reader_init (&reader, buf, n_read); + line_reader_init (&reader, (char *)buf->data, n_read); while ((line = line_reader_next (&reader, &line_len))) { DeviceUsage dev = {0}; @@ -229,12 +231,12 @@ sysprof_network_usage_record_fiber (gpointer user_data) gint64 combined_rx = 0; gint64 combined_tx = 0; - n_read = dex_await_int64 (dex_aio_read (NULL, stat_fd, buf, sizeof buf, 0), &error); + n_read = dex_await_int64 (dex_aio_read (NULL, stat_fd, buf->data, buf->len, 0), &error); if (n_read <= 0) break; lineno = 0; - line_reader_init (&reader, buf, n_read); + line_reader_init (&reader, (char *)buf->data, n_read); while ((line = line_reader_next (&reader, &line_len))) { DeviceUsage *dev; From c5af1f95c63f8b380dfbf11b54733ec5b8626303 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 6 Jul 2023 11:18:46 -0700 Subject: [PATCH 0715/1030] libsysprof-gtk: allow tracks to format values for display --- src/libsysprof-gtk/sysprof-session-discover.c | 25 ++++++++++++++++++- src/libsysprof-gtk/sysprof-session-private.h | 2 ++ src/libsysprof-gtk/sysprof-session.c | 8 +++++- src/libsysprof-gtk/sysprof-track-private.h | 8 +++--- src/libsysprof-gtk/sysprof-track.c | 24 ++++++++++++++++++ src/libsysprof-gtk/sysprof-tracks-view.c | 6 +++-- 6 files changed, 66 insertions(+), 7 deletions(-) diff --git a/src/libsysprof-gtk/sysprof-session-discover.c b/src/libsysprof-gtk/sysprof-session-discover.c index bfa92809..a933938b 100644 --- a/src/libsysprof-gtk/sysprof-session-discover.c +++ b/src/libsysprof-gtk/sysprof-session-discover.c @@ -38,6 +38,7 @@ typedef enum _LineFlags LINE_FLAGS_NO_SPLINE = 1 << 1, LINE_FLAGS_FILL = 1 << 2, LINE_FLAGS_IGNORE_RANGE_ON_SUBCHART = 1 << 3, + LINE_FLAGS_FORMAT_MEMORY = 1 << 4, } LineFlags; typedef struct _SysprofTrackSamples @@ -102,7 +103,8 @@ static const SysprofTrackCounter discovery_counters[] = { { N_("Memory"), "Memory", "Used", NULL, NULL, - LINE_FLAGS_FILL, + .0, .0, + LINE_FLAGS_FILL | LINE_FLAGS_FORMAT_MEMORY, }, { N_("FPS"), "gtk", "fps", "gtk", "*", @@ -383,6 +385,15 @@ create_chart_for_counters (SysprofTrack *track, return GTK_WIDGET (chart); } +static char * +format_value_as_memory (SysprofTrack *track, + SysprofDocumentCounterValue *value, + gpointer user_data) +{ + gint64 v = sysprof_document_counter_value_get_value_int64 (value); + return g_format_size (v); +} + static void sysprof_session_discover_counters (SysprofSession *self, SysprofDocument *document, @@ -421,6 +432,12 @@ sysprof_session_discover_counters (SysprofSession *self, "title", info->track_name, NULL); + if ((info->flags & LINE_FLAGS_FORMAT_MEMORY) != 0) + g_signal_connect (track, + "format-item-for-display", + G_CALLBACK (format_value_as_memory), + NULL); + g_signal_connect_data (track, "create-chart", G_CALLBACK (create_chart_for_counters), @@ -453,6 +470,12 @@ sysprof_session_discover_counters (SysprofSession *self, "title", sysprof_document_counter_get_name (subtrack_counter), NULL); + if ((info->flags & LINE_FLAGS_FORMAT_MEMORY) != 0) + g_signal_connect (subtrack, + "format-item-for-display", + G_CALLBACK (format_value_as_memory), + NULL); + g_signal_connect_data (subtrack, "create-chart", G_CALLBACK (create_chart_for_counters), diff --git a/src/libsysprof-gtk/sysprof-session-private.h b/src/libsysprof-gtk/sysprof-session-private.h index 561f1cc5..9fbbd4b5 100644 --- a/src/libsysprof-gtk/sysprof-session-private.h +++ b/src/libsysprof-gtk/sysprof-session-private.h @@ -21,6 +21,7 @@ #pragma once #include "sysprof-session.h" +#include "sysprof-track.h" G_BEGIN_DECLS @@ -28,6 +29,7 @@ void _sysprof_session_discover_tracks (SysprofSession *session, SysprofDocument *document, GListStore *tracks); char *_sysprof_session_describe (SysprofSession *self, + SysprofTrack *track, gpointer item); G_END_DECLS diff --git a/src/libsysprof-gtk/sysprof-session.c b/src/libsysprof-gtk/sysprof-session.c index 8238f3f4..2ec38d0a 100644 --- a/src/libsysprof-gtk/sysprof-session.c +++ b/src/libsysprof-gtk/sysprof-session.c @@ -23,7 +23,7 @@ #include #include "sysprof-session-private.h" -#include "sysprof-track.h" +#include "sysprof-track-private.h" #include "sysprof-value-axis.h" struct _SysprofSession @@ -420,13 +420,19 @@ append_time_string (GString *str, char * _sysprof_session_describe (SysprofSession *self, + SysprofTrack *track, gpointer item) { + g_autofree char *text = NULL; + g_return_val_if_fail (SYSPROF_IS_SESSION (self), NULL); if (self->document == NULL) return NULL; + if ((text = _sysprof_track_format_item_for_display (track, item))) + return g_steal_pointer (&text); + if (SYSPROF_IS_DOCUMENT_MARK (item)) { SysprofDocumentMark *mark = item; diff --git a/src/libsysprof-gtk/sysprof-track-private.h b/src/libsysprof-gtk/sysprof-track-private.h index cae00540..fdc61c75 100644 --- a/src/libsysprof-gtk/sysprof-track-private.h +++ b/src/libsysprof-gtk/sysprof-track-private.h @@ -24,8 +24,10 @@ G_BEGIN_DECLS -GtkWidget *_sysprof_track_create_chart (SysprofTrack *track); -void _sysprof_track_add_subtrack (SysprofTrack *self, - SysprofTrack *subtrack); +GtkWidget *_sysprof_track_create_chart (SysprofTrack *self); +void _sysprof_track_add_subtrack (SysprofTrack *self, + SysprofTrack *subtrack); +char *_sysprof_track_format_item_for_display (SysprofTrack *self, + gpointer item); G_END_DECLS diff --git a/src/libsysprof-gtk/sysprof-track.c b/src/libsysprof-gtk/sysprof-track.c index 9e902008..1a91a4eb 100644 --- a/src/libsysprof-gtk/sysprof-track.c +++ b/src/libsysprof-gtk/sysprof-track.c @@ -42,6 +42,7 @@ enum { enum { CREATE_CHART, + FORMAT_ITEM_FOR_DISPLAY, N_SIGNALS }; @@ -160,6 +161,15 @@ sysprof_track_class_init (SysprofTrackClass *klass) g_signal_accumulator_first_wins, NULL, NULL, GTK_TYPE_WIDGET, 0); + + signals[FORMAT_ITEM_FOR_DISPLAY] = + g_signal_new ("format-item-for-display", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + g_signal_accumulator_first_wins, NULL, + NULL, + G_TYPE_STRING, 1, G_TYPE_OBJECT); } static void @@ -245,3 +255,17 @@ _sysprof_track_create_chart (SysprofTrack *self) return ret; } + +char * +_sysprof_track_format_item_for_display (SysprofTrack *self, + gpointer item) +{ + char *ret = NULL; + + g_return_val_if_fail (SYSPROF_IS_TRACK (self), NULL); + g_return_val_if_fail (G_IS_OBJECT (item), NULL); + + g_signal_emit (self, signals[FORMAT_ITEM_FOR_DISPLAY], 0, item, &ret); + + return ret; +} diff --git a/src/libsysprof-gtk/sysprof-tracks-view.c b/src/libsysprof-gtk/sysprof-tracks-view.c index 1f2fe29b..1fb1dae4 100644 --- a/src/libsysprof-gtk/sysprof-tracks-view.c +++ b/src/libsysprof-gtk/sysprof-tracks-view.c @@ -94,9 +94,11 @@ set_motion (SysprofTracksView *self, if (pick != NULL) { SysprofChartLayer *layer = SYSPROF_CHART_LAYER (gtk_widget_get_ancestor (pick, SYSPROF_TYPE_CHART_LAYER)); + SysprofTrackView *track_view = SYSPROF_TRACK_VIEW (gtk_widget_get_ancestor (pick, SYSPROF_TYPE_TRACK_VIEW)); - if (layer != NULL) + if (layer != NULL && track_view != NULL) { + SysprofTrack *track = sysprof_track_view_get_track (track_view); g_autoptr(GObject) item = NULL; g_autofree char *text = NULL; double layer_x; @@ -107,7 +109,7 @@ set_motion (SysprofTracksView *self, x, y, &layer_x, &layer_y); if ((item = sysprof_chart_layer_lookup_item (layer, layer_x, layer_y))) - text = _sysprof_session_describe (self->session, item); + text = _sysprof_session_describe (self->session, track, item); gtk_label_set_label (self->informative, text); From a0d5f0fc6699807314671e035ea7ba696b47536d Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 6 Jul 2023 11:18:53 -0700 Subject: [PATCH 0716/1030] libsysprof-profile: set cwd when spawning process --- src/libsysprof-profile/tests/test-profiler.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libsysprof-profile/tests/test-profiler.c b/src/libsysprof-profile/tests/test-profiler.c index 10debb11..271d8b27 100644 --- a/src/libsysprof-profile/tests/test-profiler.c +++ b/src/libsysprof-profile/tests/test-profiler.c @@ -197,6 +197,8 @@ main (int argc, g_autoptr(SysprofSpawnable) spawnable = sysprof_spawnable_new (); sysprof_spawnable_append_args (spawnable, (const char * const *)&argv[i+1]); + sysprof_spawnable_set_cwd (spawnable, g_get_current_dir ()); + sysprof_profiler_set_spawnable (profiler, spawnable); trace_fd = sysprof_spawnable_add_trace_fd (spawnable, NULL); From 5884636da57e3b8557095d815ec4363a4c67ec84 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 6 Jul 2023 11:32:23 -0700 Subject: [PATCH 0717/1030] libsysprof-analyze: add some string helpers --- src/libsysprof-analyze/sysprof-time-span.h | 31 ++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-time-span.h b/src/libsysprof-analyze/sysprof-time-span.h index 0be78b96..d76b73e7 100644 --- a/src/libsysprof-analyze/sysprof-time-span.h +++ b/src/libsysprof-analyze/sysprof-time-span.h @@ -99,4 +99,35 @@ sysprof_time_span_clamp (SysprofTimeSpan *time_span, return TRUE; } +static inline char * +sysprof_time_offset_to_string (gint64 o) +{ + char str[32]; + + if (o == 0) + g_snprintf (str, sizeof str, "%.3lfs", .0); + else if (o < 1000000) + g_snprintf (str, sizeof str, "%.3lfμs", o/1000.); + else if (o < SYSPROF_NSEC_PER_SEC) + g_snprintf (str, sizeof str, "%.3lfms", o/1000000.); + else + g_snprintf (str, sizeof str, "%.3lfs", o/(double)SYSPROF_NSEC_PER_SEC); + + return g_strdup (str); +} + +static inline char * +sysprof_time_span_to_string (const SysprofTimeSpan *span) +{ + g_autofree char *begin = sysprof_time_offset_to_string (span->begin_nsec); + g_autofree char *end = NULL; + + if (span->end_nsec == span->begin_nsec) + return g_steal_pointer (&begin); + + end = sysprof_time_offset_to_string (span->end_nsec - span->begin_nsec); + + return g_strdup_printf ("%s (%s)", begin, end); +} + G_END_DECLS From 125ad56af5ff667dffda8e8118bcc460e29a4580 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 6 Jul 2023 11:32:38 -0700 Subject: [PATCH 0718/1030] libsysprof-gtk: include time when formatting memory value --- src/libsysprof-gtk/sysprof-session-discover.c | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/src/libsysprof-gtk/sysprof-session-discover.c b/src/libsysprof-gtk/sysprof-session-discover.c index a933938b..0c6afb30 100644 --- a/src/libsysprof-gtk/sysprof-session-discover.c +++ b/src/libsysprof-gtk/sysprof-session-discover.c @@ -388,10 +388,15 @@ create_chart_for_counters (SysprofTrack *track, static char * format_value_as_memory (SysprofTrack *track, SysprofDocumentCounterValue *value, - gpointer user_data) + SysprofDocument *document) { + const SysprofTimeSpan *span = sysprof_document_get_time_span (document); gint64 v = sysprof_document_counter_value_get_value_int64 (value); - return g_format_size (v); + gint64 t = sysprof_document_counter_value_get_time (value) - span->begin_nsec; + g_autofree char *vstr = g_format_size (v); + g_autofree char *tstr = sysprof_time_offset_to_string (t); + + return g_strdup_printf ("%s: %s", tstr, vstr); } static void @@ -433,10 +438,11 @@ sysprof_session_discover_counters (SysprofSession *self, NULL); if ((info->flags & LINE_FLAGS_FORMAT_MEMORY) != 0) - g_signal_connect (track, - "format-item-for-display", - G_CALLBACK (format_value_as_memory), - NULL); + g_signal_connect_object (track, + "format-item-for-display", + G_CALLBACK (format_value_as_memory), + document, + 0); g_signal_connect_data (track, "create-chart", @@ -471,10 +477,11 @@ sysprof_session_discover_counters (SysprofSession *self, NULL); if ((info->flags & LINE_FLAGS_FORMAT_MEMORY) != 0) - g_signal_connect (subtrack, - "format-item-for-display", - G_CALLBACK (format_value_as_memory), - NULL); + g_signal_connect_object (subtrack, + "format-item-for-display", + G_CALLBACK (format_value_as_memory), + document, + 0); g_signal_connect_data (subtrack, "create-chart", From c3e0235d99087b21505d825ec3b42c86f0fd4d06 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 6 Jul 2023 11:44:00 -0700 Subject: [PATCH 0719/1030] libsysprof-gtk: add some test menus --- src/libsysprof-gtk/tests/test-tracks.ui | 29 +++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/libsysprof-gtk/tests/test-tracks.ui b/src/libsysprof-gtk/tests/test-tracks.ui index 8c97cefe..2607a0a9 100644 --- a/src/libsysprof-gtk/tests/test-tracks.ui +++ b/src/libsysprof-gtk/tests/test-tracks.ui @@ -28,6 +28,7 @@ + omni_menu 25 @@ -191,4 +192,32 @@ + +
+ Profiling + Sample Native Stacks + Sample JavaScript Stacks + Trace Memory Allocations +
+
+ Graphics + Display Timings +
+
+ Counters + CPU Usage + Memory Usage + Storage I/O + Network I/O +
+
+ Energy + Consumption + Battery Charge +
+
+ Allow Application Integration + Allow CPU Throttling +
+
From c0a7a94d5238351acdf525758b1216823c777355 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 6 Jul 2023 12:12:25 -0700 Subject: [PATCH 0720/1030] libsysprof-analyze: add flag to ignore system libraries --- src/libsysprof-analyze/sysprof-callgraph.c | 12 +++++- src/libsysprof-analyze/sysprof-callgraph.h | 5 ++- src/libsysprof-analyze/sysprof-elf.c | 35 +++++++++------- .../sysprof-symbol-private.h | 27 ++++++++++++ .../sysprof-callgraph-view-private.h | 1 + src/libsysprof-gtk/sysprof-callgraph-view.c | 41 +++++++++++++++++++ src/libsysprof-gtk/sysprof-callgraph-view.h | 27 +++++++----- src/libsysprof-gtk/tests/test-callgraph.c | 10 +++++ src/libsysprof-gtk/tests/test-tracks.ui | 5 +-- 9 files changed, 131 insertions(+), 32 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-callgraph.c b/src/libsysprof-analyze/sysprof-callgraph.c index b6461dad..4af6ca0a 100644 --- a/src/libsysprof-analyze/sysprof-callgraph.c +++ b/src/libsysprof-analyze/sysprof-callgraph.c @@ -213,7 +213,8 @@ static SysprofCallgraphNode * sysprof_callgraph_add_trace (SysprofCallgraph *self, SysprofSymbol **symbols, guint n_symbols, - guint list_model_index) + guint list_model_index, + gboolean hide_system_libraries) { SysprofCallgraphNode *parent = NULL; @@ -238,6 +239,9 @@ sysprof_callgraph_add_trace (SysprofCallgraph *self, SysprofSymbol *symbol = symbols[i-1]; SysprofCallgraphNode *node = NULL; + if (hide_system_libraries && _sysprof_symbol_is_system_library (symbol)) + continue; + /* Try to find @symbol within the children of @parent */ for (SysprofCallgraphNode *iter = parent->children; iter != NULL; @@ -335,7 +339,11 @@ sysprof_callgraph_add_traceable (SysprofCallgraph *self, symbols[n_symbols++] = _sysprof_document_process_symbol (self->document, pid); symbols[n_symbols++] = everything; - node = sysprof_callgraph_add_trace (self, symbols, n_symbols, list_model_index); + node = sysprof_callgraph_add_trace (self, + symbols, + n_symbols, + list_model_index, + !!(self->flags & SYSPROF_CALLGRAPH_FLAGS_HIDE_SYSTEM_LIBRARIES)); if (node && self->augment_func) self->augment_func (self, diff --git a/src/libsysprof-analyze/sysprof-callgraph.h b/src/libsysprof-analyze/sysprof-callgraph.h index 4f476d31..6c77bd1e 100644 --- a/src/libsysprof-analyze/sysprof-callgraph.h +++ b/src/libsysprof-analyze/sysprof-callgraph.h @@ -64,8 +64,9 @@ typedef void (*SysprofAugmentationFunc) (SysprofCallgraph *callgraph, typedef enum _SysprofCallgraphFlags { - SYSPROF_CALLGRAPH_FLAGS_NONE = 0, - SYSPROF_CALLGRAPH_FLAGS_INCLUDE_THREADS = 1 << 1, + SYSPROF_CALLGRAPH_FLAGS_NONE = 0, + SYSPROF_CALLGRAPH_FLAGS_INCLUDE_THREADS = 1 << 1, + SYSPROF_CALLGRAPH_FLAGS_HIDE_SYSTEM_LIBRARIES = 1 << 2, } SysprofCallgraphFlags; SYSPROF_AVAILABLE_IN_ALL diff --git a/src/libsysprof-analyze/sysprof-elf.c b/src/libsysprof-analyze/sysprof-elf.c index 0a3a6bad..98b2b9b6 100644 --- a/src/libsysprof-analyze/sysprof-elf.c +++ b/src/libsysprof-analyze/sysprof-elf.c @@ -54,32 +54,18 @@ static const struct { const char *nick; } nick_table[] = { { "libc.so", "libc" }, - { "libcairo-gobject.so", "Cairo" }, - { "libcairo.so", "Cairo" }, - { "libclutter-1.0.so", "Clutter" }, - { "libclutter-glx-1.0.so", "Clutter" }, { "libffi.so", "libffi" }, - { "libgjs.so", "GJS" }, - { "libgstreamer-1-0.so", "GStreamer" }, - { "libgudev-1.0.so", "udev" }, - { "libibus-1.0.so", "IBus" }, - { "libinput.so", "Mutter" }, { "ld-linux-x86-64.so", "glibc" }, { "libnss_sss.so", "NSS" }, { "libsss_debug.so", "SSSD" }, { "libsss_util.so", "SSSD" }, { "libnss_systemd.so", "NSS" }, { "libpcre2-8.so", "PCRE" }, - { "libpixman-1.so", "Pixman" }, - { "libpolkit-agent-1.so", "PolicyKit" }, - { "libpolkit-gobject-1.so", "PolicyKit" }, { "libselinux.so", "SELinux" }, { "libssl3.so", "NSS" }, { "libstdc++.so", "libc" }, { "libsystemd.so", "systemd" }, { "libudev.so", "udev" }, - { "libvte-2.91.so", "VTE" }, - { "libvte-2.91-gtk4.so", "VTE" }, { "libxul.so", "XUL" }, { "libz.so", "Zlib" }, { "libzstd.so", "Zstd" }, @@ -90,6 +76,24 @@ static const struct { { "libgio-2.0.so", "Gio" }, { "libgirepository-1.0.so", "Introspection" }, + /* Cairo/Pixman */ + { "libcairo-gobject.so", "Cairo" }, + { "libcairo.so", "Cairo" }, + { "libpixman-1.so", "Pixman" }, + + /* Various GNOME Platform Libraries */ + { "libdex-1.so", "Dex" }, + { "libgjs.so", "GJS" }, + { "libgstreamer-1-0.so", "GStreamer" }, + { "libgudev-1.0.so", "udev" }, + { "libibus-1.0.so", "IBus" }, + { "libjson-glib-1.0.so", "JSON-GLib" }, + { "libjsonrpc-glib-1.0.so", "JSONRPC-GLib" }, + { "libpolkit-agent-1.so", "PolicyKit" }, + { "libpolkit-gobject-1.so", "PolicyKit" }, + { "libvte-2.91-gtk4.so", "VTE" }, + { "libvte-2.91.so", "VTE" }, + /* Pango and Harfbuzz */ { "libfribidi.so", "Fribidi" }, { "libpango-1.0.so", "Pango" }, @@ -122,6 +126,9 @@ static const struct { { "libwayland-server.so", "Wayland Server" }, /* Mutter/Clutter/Shell */ + { "libclutter-1.0.so", "Clutter" }, + { "libclutter-glx-1.0.so", "Clutter" }, + { "libinput.so", "libinput" }, { "libmutter-12.so", "Mutter" }, { "libmutter-cogl-12.so", "Mutter" }, { "libmutter-clutter-12.so", "Mutter" }, diff --git a/src/libsysprof-analyze/sysprof-symbol-private.h b/src/libsysprof-analyze/sysprof-symbol-private.h index 3f641ac4..545d9f48 100644 --- a/src/libsysprof-analyze/sysprof-symbol-private.h +++ b/src/libsysprof-analyze/sysprof-symbol-private.h @@ -85,4 +85,31 @@ _sysprof_symbol_is_context_switch (SysprofSymbol *symbol) return symbol->kind == SYSPROF_SYMBOL_KIND_CONTEXT_SWITCH; } +static inline gboolean +_sysprof_symbol_is_system_library (SysprofSymbol *symbol) +{ + switch (symbol->kind) + { + case SYSPROF_SYMBOL_KIND_ROOT: + case SYSPROF_SYMBOL_KIND_PROCESS: + case SYSPROF_SYMBOL_KIND_THREAD: + case SYSPROF_SYMBOL_KIND_CONTEXT_SWITCH: + case SYSPROF_SYMBOL_KIND_UNWINDABLE: + default: + return FALSE; + + case SYSPROF_SYMBOL_KIND_KERNEL: + return TRUE; + + case SYSPROF_SYMBOL_KIND_USER: + if (symbol->binary_nick != NULL) + return TRUE; + + if (symbol->binary_path != NULL) + return !!strstr (symbol->binary_path, "/usr/"); + + return FALSE; + } +} + G_END_DECLS diff --git a/src/libsysprof-gtk/sysprof-callgraph-view-private.h b/src/libsysprof-gtk/sysprof-callgraph-view-private.h index 55ce88b8..721f0aed 100644 --- a/src/libsysprof-gtk/sysprof-callgraph-view-private.h +++ b/src/libsysprof-gtk/sysprof-callgraph-view-private.h @@ -52,6 +52,7 @@ struct _SysprofCallgraphView guint reload_source; guint include_threads : 1; + guint hide_system_libraries : 1; }; struct _SysprofCallgraphViewClass diff --git a/src/libsysprof-gtk/sysprof-callgraph-view.c b/src/libsysprof-gtk/sysprof-callgraph-view.c index f66758c7..bca4ee3e 100644 --- a/src/libsysprof-gtk/sysprof-callgraph-view.c +++ b/src/libsysprof-gtk/sysprof-callgraph-view.c @@ -31,6 +31,7 @@ enum { PROP_0, PROP_CALLGRAPH, PROP_DOCUMENT, + PROP_HIDE_SYSTEM_LIBRARIES, PROP_INCLUDE_THREADS, PROP_TRACEABLES, N_PROPS @@ -385,6 +386,10 @@ sysprof_callgraph_view_get_property (GObject *object, g_value_set_object (value, sysprof_callgraph_view_get_document (self)); break; + case PROP_HIDE_SYSTEM_LIBRARIES: + g_value_set_boolean (value, sysprof_callgraph_view_get_hide_system_libraries (self)); + break; + case PROP_INCLUDE_THREADS: g_value_set_boolean (value, sysprof_callgraph_view_get_include_threads (self)); break; @@ -412,6 +417,10 @@ sysprof_callgraph_view_set_property (GObject *object, sysprof_callgraph_view_set_document (self, g_value_get_object (value)); break; + case PROP_HIDE_SYSTEM_LIBRARIES: + sysprof_callgraph_view_set_hide_system_libraries (self, g_value_get_boolean (value)); + break; + case PROP_INCLUDE_THREADS: sysprof_callgraph_view_set_include_threads (self, g_value_get_boolean (value)); break; @@ -445,6 +454,11 @@ sysprof_callgraph_view_class_init (SysprofCallgraphViewClass *klass) SYSPROF_TYPE_DOCUMENT, (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + properties[PROP_HIDE_SYSTEM_LIBRARIES] = + g_param_spec_boolean ("hide-system-libraries", NULL, NULL, + FALSE, + (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + properties[PROP_INCLUDE_THREADS] = g_param_spec_boolean ("include-threads", NULL, NULL, FALSE, @@ -590,6 +604,9 @@ sysprof_callgraph_view_reload (SysprofCallgraphView *self) if (self->include_threads) flags |= SYSPROF_CALLGRAPH_FLAGS_INCLUDE_THREADS; + if (self->hide_system_libraries) + flags |= SYSPROF_CALLGRAPH_FLAGS_HIDE_SYSTEM_LIBRARIES; + sysprof_document_callgraph_async (self->document, flags, self->traceables, @@ -724,6 +741,30 @@ sysprof_callgraph_view_get_callgraph (SysprofCallgraphView *self) return self->callgraph; } +gboolean +sysprof_callgraph_view_get_hide_system_libraries (SysprofCallgraphView *self) +{ + g_return_val_if_fail (SYSPROF_IS_CALLGRAPH_VIEW (self), FALSE); + + return self->hide_system_libraries; +} + +void +sysprof_callgraph_view_set_hide_system_libraries (SysprofCallgraphView *self, + gboolean hide_system_libraries) +{ + g_return_if_fail (SYSPROF_IS_CALLGRAPH_VIEW (self)); + + hide_system_libraries = !!hide_system_libraries; + + if (self->hide_system_libraries != hide_system_libraries) + { + self->hide_system_libraries = hide_system_libraries; + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_HIDE_SYSTEM_LIBRARIES]); + sysprof_callgraph_view_queue_reload (self); + } +} + gboolean sysprof_callgraph_view_get_include_threads (SysprofCallgraphView *self) { diff --git a/src/libsysprof-gtk/sysprof-callgraph-view.h b/src/libsysprof-gtk/sysprof-callgraph-view.h index 0d6074fe..596ae1ab 100644 --- a/src/libsysprof-gtk/sysprof-callgraph-view.h +++ b/src/libsysprof-gtk/sysprof-callgraph-view.h @@ -36,24 +36,29 @@ typedef struct _SysprofCallgraphView SysprofCallgraphView; typedef struct _SysprofCallgraphViewClass SysprofCallgraphViewClass; SYSPROF_AVAILABLE_IN_ALL -GType sysprof_callgraph_view_get_type (void) G_GNUC_CONST; +GType sysprof_callgraph_view_get_type (void) G_GNUC_CONST; SYSPROF_AVAILABLE_IN_ALL -SysprofCallgraph *sysprof_callgraph_view_get_callgraph (SysprofCallgraphView *self); +SysprofCallgraph *sysprof_callgraph_view_get_callgraph (SysprofCallgraphView *self); SYSPROF_AVAILABLE_IN_ALL -SysprofDocument *sysprof_callgraph_view_get_document (SysprofCallgraphView *self); +SysprofDocument *sysprof_callgraph_view_get_document (SysprofCallgraphView *self); SYSPROF_AVAILABLE_IN_ALL -void sysprof_callgraph_view_set_document (SysprofCallgraphView *self, - SysprofDocument *document); +void sysprof_callgraph_view_set_document (SysprofCallgraphView *self, + SysprofDocument *document); SYSPROF_AVAILABLE_IN_ALL -GListModel *sysprof_callgraph_view_get_traceables (SysprofCallgraphView *self); +GListModel *sysprof_callgraph_view_get_traceables (SysprofCallgraphView *self); SYSPROF_AVAILABLE_IN_ALL -void sysprof_callgraph_view_set_traceables (SysprofCallgraphView *self, - GListModel *model); +void sysprof_callgraph_view_set_traceables (SysprofCallgraphView *self, + GListModel *model); SYSPROF_AVAILABLE_IN_ALL -gboolean sysprof_callgraph_view_get_include_threads (SysprofCallgraphView *self); +gboolean sysprof_callgraph_view_get_include_threads (SysprofCallgraphView *self); SYSPROF_AVAILABLE_IN_ALL -void sysprof_callgraph_view_set_include_threads (SysprofCallgraphView *self, - gboolean include_threads); +void sysprof_callgraph_view_set_include_threads (SysprofCallgraphView *self, + gboolean include_threads); +SYSPROF_AVAILABLE_IN_ALL +gboolean sysprof_callgraph_view_get_hide_system_libraries (SysprofCallgraphView *self); +SYSPROF_AVAILABLE_IN_ALL +void sysprof_callgraph_view_set_hide_system_libraries (SysprofCallgraphView *self, + gboolean hide_system_libraries); G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofCallgraphView, g_object_unref) diff --git a/src/libsysprof-gtk/tests/test-callgraph.c b/src/libsysprof-gtk/tests/test-callgraph.c index dc6b71bb..ab50b68a 100644 --- a/src/libsysprof-gtk/tests/test-callgraph.c +++ b/src/libsysprof-gtk/tests/test-callgraph.c @@ -29,9 +29,11 @@ static GMainLoop *main_loop; static char *kallsyms_path; static char *filename; static gboolean include_threads; +static gboolean hide_system_libraries; static const GOptionEntry entries[] = { { "kallsyms", 'k', 0, G_OPTION_ARG_FILENAME, &kallsyms_path, "The path to kallsyms to use for decoding", "PATH" }, { "threads", 't', 0, G_OPTION_ARG_NONE, &include_threads, "Include threads in the callgraph" }, + { "hide-system-libraries", 's', 0, G_OPTION_ARG_NONE, &hide_system_libraries, "Hide system libraries in the callgraph" }, { 0 } }; @@ -73,6 +75,7 @@ main (int argc, GtkWidget *progress; GtkWidget *message; GtkWidget *threads; + GtkWidget *system_libs; GtkWindow *window; sysprof_clock_init (); @@ -134,8 +137,14 @@ main (int argc, "active", include_threads, NULL); gtk_box_append (GTK_BOX (hbox), threads); + gtk_box_append (GTK_BOX (hbox), gtk_label_new ("Hide System Libraries")); + system_libs = g_object_new (GTK_TYPE_SWITCH, + "active", hide_system_libraries, + NULL); + gtk_box_append (GTK_BOX (hbox), system_libs); view = g_object_new (SYSPROF_TYPE_WEIGHTED_CALLGRAPH_VIEW, "include-threads", include_threads, + "hide-system-libraries", hide_system_libraries, "vexpand", TRUE, NULL); gtk_box_append (GTK_BOX (box), GTK_WIDGET (view)); @@ -159,6 +168,7 @@ main (int argc, gtk_window_present (window); g_object_bind_property (threads, "active", view, "include-threads", 0); + g_object_bind_property (system_libs, "active", view, "hide-system-libraries", 0); g_object_bind_property (loader, "message", message, "label", 0); g_object_bind_property (loader, "fraction", progress, "fraction", 0); diff --git a/src/libsysprof-gtk/tests/test-tracks.ui b/src/libsysprof-gtk/tests/test-tracks.ui index 2607a0a9..0c9bce12 100644 --- a/src/libsysprof-gtk/tests/test-tracks.ui +++ b/src/libsysprof-gtk/tests/test-tracks.ui @@ -187,9 +187,8 @@
Callgraph - - Show Threads - + Show Threads + Show System Libraries
From 65a567f0d18a19f1b927359cc6c760afa6542687 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 6 Jul 2023 14:51:41 -0700 Subject: [PATCH 0721/1030] libsysprof-analyze: cleanup some signed-int bswap --- src/libsysprof-analyze/sysprof-document.c | 32 +++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index 3f62837f..9b6b29f0 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -115,6 +115,34 @@ enum { static GParamSpec *properties[N_PROPS]; +static inline int +swap_int32 (gboolean needs_swap, + int value) +{ + if (!needs_swap) + return value; + +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + return GINT32_FROM_BE (value); +#else + return GINT32_FROM_LE (value); +#endif +} + +static inline int +swap_int64 (gboolean needs_swap, + gint64 value) +{ + if (!needs_swap) + return value; + +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + return GINT64_FROM_BE (value); +#else + return GINT64_FROM_LE (value); +#endif +} + static GType sysprof_document_get_item_type (GListModel *model) { @@ -951,8 +979,8 @@ sysprof_document_load_worker (GTask *task, { SysprofDocumentFramePointer *ptr = &g_array_index (self->frames, SysprofDocumentFramePointer, f); const SysprofCaptureFrame *tainted = (const SysprofCaptureFrame *)(gpointer)&self->base[ptr->offset]; - gint64 t = self->needs_swap ? GUINT64_SWAP_LE_BE (tainted->time) : tainted->time; - int pid = self->needs_swap ? GUINT32_SWAP_LE_BE (tainted->pid) : tainted->pid; + gint64 t = swap_int64 (self->needs_swap, tainted->time); + int pid = swap_int32 (self->needs_swap, tainted->pid); if (t > guessed_end_nsec && is_data_type (tainted->type)) guessed_end_nsec = t; From 2b29ce8e730d202595973b9be06f61f269c794ca Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 6 Jul 2023 14:52:02 -0700 Subject: [PATCH 0722/1030] libsysprof-analyze: record exit time of processes This will allow us to treat them as a duration in a chart. --- src/libsysprof-analyze/sysprof-document.c | 7 +++++++ src/libsysprof-analyze/sysprof-process-info-private.h | 1 + 2 files changed, 8 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index 9b6b29f0..cb70e519 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -1069,6 +1069,13 @@ sysprof_document_load_worker (GTask *task, } } } + else if (tainted->type == SYSPROF_CAPTURE_FRAME_EXIT) + { + SysprofProcessInfo *info = g_hash_table_lookup (self->pid_to_process_info, GINT_TO_POINTER (pid)); + + if (info != NULL) + info->exit_time = t; + } else if (tainted->type == SYSPROF_CAPTURE_FRAME_MARK) { const SysprofCaptureMark *mark = (const SysprofCaptureMark *)tainted; diff --git a/src/libsysprof-analyze/sysprof-process-info-private.h b/src/libsysprof-analyze/sysprof-process-info-private.h index ca0647de..9e623c0e 100644 --- a/src/libsysprof-analyze/sysprof-process-info-private.h +++ b/src/libsysprof-analyze/sysprof-process-info-private.h @@ -34,6 +34,7 @@ typedef struct _SysprofProcessInfo SysprofSymbol *fallback_symbol; SysprofSymbol *symbol; int pid; + gint64 exit_time; } SysprofProcessInfo; SysprofProcessInfo *sysprof_process_info_new (SysprofMountNamespace *mount_namespace, From 58a089fb94640300620008c32b32533a7f7a534e Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 6 Jul 2023 14:55:07 -0700 Subject: [PATCH 0723/1030] libsysprof-analyze: expose exit-time as a property --- .../sysprof-document-process.c | 21 +++++++++++++++++++ .../sysprof-document-process.h | 2 ++ 2 files changed, 23 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-document-process.c b/src/libsysprof-analyze/sysprof-document-process.c index bb5feb04..f201ea55 100644 --- a/src/libsysprof-analyze/sysprof-document-process.c +++ b/src/libsysprof-analyze/sysprof-document-process.c @@ -38,6 +38,7 @@ struct _SysprofDocumentProcessClass enum { PROP_0, PROP_COMMAND_LINE, + PROP_EXIT_TIME, N_PROPS }; @@ -69,6 +70,10 @@ sysprof_document_process_get_property (GObject *object, g_value_set_string (value, sysprof_document_process_get_command_line (self)); break; + case PROP_EXIT_TIME: + g_value_set_int64 (value, sysprof_document_process_get_exit_time (self)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } @@ -87,6 +92,11 @@ sysprof_document_process_class_init (SysprofDocumentProcessClass *klass) NULL, (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + properties [PROP_EXIT_TIME] = + g_param_spec_int64 ("exit-time", NULL, NULL, + G_MININT64, G_MAXINT64, 0, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_properties (object_class, N_PROPS, properties); } @@ -163,3 +173,14 @@ _sysprof_document_process_set_info (SysprofDocumentProcess *self, self->process_info = sysprof_process_info_ref (process_info); } + +gint64 +sysprof_document_process_get_exit_time (SysprofDocumentProcess *self) +{ + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_PROCESS (self), 0); + + if (self->process_info != NULL) + return self->process_info->exit_time; + + return 0; +} diff --git a/src/libsysprof-analyze/sysprof-document-process.h b/src/libsysprof-analyze/sysprof-document-process.h index 83ce34fa..78c67a9b 100644 --- a/src/libsysprof-analyze/sysprof-document-process.h +++ b/src/libsysprof-analyze/sysprof-document-process.h @@ -39,6 +39,8 @@ GType sysprof_document_process_get_type (void) G_GNUC_CONST; SYSPROF_AVAILABLE_IN_ALL const char *sysprof_document_process_get_command_line (SysprofDocumentProcess *self); SYSPROF_AVAILABLE_IN_ALL +gint64 sysprof_document_process_get_exit_time (SysprofDocumentProcess *self); +SYSPROF_AVAILABLE_IN_ALL GListModel *sysprof_document_process_list_memory_maps (SysprofDocumentProcess *self); SYSPROF_AVAILABLE_IN_ALL GListModel *sysprof_document_process_list_mounts (SysprofDocumentProcess *self); From f7bdebe1a8a5ec2bf213de407eac69711abfa420 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 6 Jul 2023 14:59:08 -0700 Subject: [PATCH 0724/1030] libsysprof-analyze: ensure all process info get an exit time --- src/libsysprof-analyze/sysprof-document.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index cb70e519..75fe50f0 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -876,6 +876,22 @@ sort_by_time_swapped (gconstpointer a, return 0; } +static void +sysprof_document_update_process_exit_times (SysprofDocument *self) +{ + GHashTableIter iter; + SysprofProcessInfo *info; + + g_assert (SYSPROF_IS_DOCUMENT (self)); + + g_hash_table_iter_init (&iter, self->pid_to_process_info); + while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&info)) + { + if (info->exit_time == 0) + info->exit_time = self->time_span.end_nsec; + } +} + static void sysprof_document_load_worker (GTask *task, gpointer source_object, @@ -1116,6 +1132,9 @@ sysprof_document_load_worker (GTask *task, if (guessed_end_nsec != 0) self->time_span.end_nsec = guessed_end_nsec; + /* Ensure all our process have an exit_time set */ + sysprof_document_update_process_exit_times (self); + load_progress (load, .6, _("Discovering file system mounts")); sysprof_document_load_mounts (self); From 51abce735c0add97c98dede308c83f17407aca43 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 6 Jul 2023 15:00:59 -0700 Subject: [PATCH 0725/1030] libsysprof-analyze: ensure exit-time is >= spawn time --- src/libsysprof-analyze/sysprof-document-process.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-document-process.c b/src/libsysprof-analyze/sysprof-document-process.c index f201ea55..9ff93209 100644 --- a/src/libsysprof-analyze/sysprof-document-process.c +++ b/src/libsysprof-analyze/sysprof-document-process.c @@ -177,10 +177,15 @@ _sysprof_document_process_set_info (SysprofDocumentProcess *self, gint64 sysprof_document_process_get_exit_time (SysprofDocumentProcess *self) { + gint64 exit_time = 0; + gint64 t; + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_PROCESS (self), 0); if (self->process_info != NULL) - return self->process_info->exit_time; + exit_time = self->process_info->exit_time; - return 0; + t = sysprof_document_frame_get_time (SYSPROF_DOCUMENT_FRAME (self)); + + return MAX (t, exit_time); } From a999a8a45555fbc47b77f12c914b02553597ce6e Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 6 Jul 2023 15:20:11 -0700 Subject: [PATCH 0726/1030] libsysprof-analyze: expose processes as property --- src/libsysprof-analyze/sysprof-document.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index 75fe50f0..50327706 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -107,6 +107,7 @@ enum { PROP_0, PROP_ALLOCATIONS, PROP_COUNTERS, + PROP_PROCESSES, PROP_SAMPLES, PROP_TIME_SPAN, PROP_TITLE, @@ -313,6 +314,10 @@ sysprof_document_get_property (GObject *object, g_value_take_object (value, sysprof_document_list_counters (self)); break; + case PROP_PROCESSES: + g_value_take_object (value, sysprof_document_list_processes (self)); + break; + case PROP_SAMPLES: g_value_take_object (value, sysprof_document_list_samples (self)); break; @@ -348,6 +353,11 @@ sysprof_document_class_init (SysprofDocumentClass *klass) G_TYPE_LIST_MODEL, (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + properties [PROP_PROCESSES] = + g_param_spec_object ("processes", NULL, NULL, + G_TYPE_LIST_MODEL, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + properties [PROP_SAMPLES] = g_param_spec_object ("samples", NULL, NULL, G_TYPE_LIST_MODEL, From b3f8b45b16e36236b211d08c703adbfe1d01de77 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 6 Jul 2023 15:33:56 -0700 Subject: [PATCH 0727/1030] libsysprof-analyze: add duration property to process This is a bit easier to bind to a chart since we need the expression as a duration rather than the end time. --- .../sysprof-document-process.c | 22 +++++++++++++++++++ .../sysprof-document-process.h | 2 ++ 2 files changed, 24 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-document-process.c b/src/libsysprof-analyze/sysprof-document-process.c index 9ff93209..9dc8c4ca 100644 --- a/src/libsysprof-analyze/sysprof-document-process.c +++ b/src/libsysprof-analyze/sysprof-document-process.c @@ -38,6 +38,7 @@ struct _SysprofDocumentProcessClass enum { PROP_0, PROP_COMMAND_LINE, + PROP_DURATION, PROP_EXIT_TIME, N_PROPS }; @@ -70,6 +71,10 @@ sysprof_document_process_get_property (GObject *object, g_value_set_string (value, sysprof_document_process_get_command_line (self)); break; + case PROP_DURATION: + g_value_set_int64 (value, sysprof_document_process_get_duration (self)); + break; + case PROP_EXIT_TIME: g_value_set_int64 (value, sysprof_document_process_get_exit_time (self)); break; @@ -92,6 +97,11 @@ sysprof_document_process_class_init (SysprofDocumentProcessClass *klass) NULL, (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + properties [PROP_DURATION] = + g_param_spec_int64 ("duration", NULL, NULL, + G_MININT64, G_MAXINT64, 0, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + properties [PROP_EXIT_TIME] = g_param_spec_int64 ("exit-time", NULL, NULL, G_MININT64, G_MAXINT64, 0, @@ -189,3 +199,15 @@ sysprof_document_process_get_exit_time (SysprofDocumentProcess *self) return MAX (t, exit_time); } + +gint64 +sysprof_document_process_get_duration (SysprofDocumentProcess *self) +{ + gint64 t; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_PROCESS (self), 0); + + t = sysprof_document_frame_get_time (SYSPROF_DOCUMENT_FRAME (self)); + + return sysprof_document_process_get_exit_time (self) - t; +} diff --git a/src/libsysprof-analyze/sysprof-document-process.h b/src/libsysprof-analyze/sysprof-document-process.h index 78c67a9b..4c498968 100644 --- a/src/libsysprof-analyze/sysprof-document-process.h +++ b/src/libsysprof-analyze/sysprof-document-process.h @@ -41,6 +41,8 @@ const char *sysprof_document_process_get_command_line (SysprofDocumentProcess *s SYSPROF_AVAILABLE_IN_ALL gint64 sysprof_document_process_get_exit_time (SysprofDocumentProcess *self); SYSPROF_AVAILABLE_IN_ALL +gint64 sysprof_document_process_get_duration (SysprofDocumentProcess *self); +SYSPROF_AVAILABLE_IN_ALL GListModel *sysprof_document_process_list_memory_maps (SysprofDocumentProcess *self); SYSPROF_AVAILABLE_IN_ALL GListModel *sysprof_document_process_list_mounts (SysprofDocumentProcess *self); From fbd52438b53d1c035fff83588fb874c67a0f2fd4 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 6 Jul 2023 15:52:20 -0700 Subject: [PATCH 0728/1030] libsysprof-gtk: fix property names --- src/libsysprof-gtk/sysprof-time-series.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libsysprof-gtk/sysprof-time-series.c b/src/libsysprof-gtk/sysprof-time-series.c index 6b7df724..17d9b98e 100644 --- a/src/libsysprof-gtk/sysprof-time-series.c +++ b/src/libsysprof-gtk/sysprof-time-series.c @@ -141,11 +141,11 @@ sysprof_time_series_class_init (SysprofTimeSeriesClass *klass) series_class->get_series_item = sysprof_time_series_get_series_item; properties [PROP_TIME_EXPRESSION] = - gtk_param_spec_expression ("x-expression", NULL, NULL, + gtk_param_spec_expression ("time-expression", NULL, NULL, (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); properties [PROP_DURATION_EXPRESSION] = - gtk_param_spec_expression ("y-expression", NULL, NULL, + gtk_param_spec_expression ("duration-expression", NULL, NULL, (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); properties [PROP_LABEL_EXPRESSION] = From 82032c3aa4624259c00103e1bd70a72ddfe69ae1 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 6 Jul 2023 15:52:28 -0700 Subject: [PATCH 0729/1030] libsysprof-gtk: bind expression to normalized series --- src/libsysprof-gtk/sysprof-time-span-layer.c | 27 +++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/libsysprof-gtk/sysprof-time-span-layer.c b/src/libsysprof-gtk/sysprof-time-span-layer.c index 1483d4a2..6dcfe2e2 100644 --- a/src/libsysprof-gtk/sysprof-time-span-layer.c +++ b/src/libsysprof-gtk/sysprof-time-span-layer.c @@ -36,6 +36,8 @@ struct _SysprofTimeSpanLayer SysprofNormalizedSeries *normal_x; SysprofNormalizedSeries *normal_x2; + GBindingGroup *series_bindings; + GdkRGBA color; GdkRGBA event_color; @@ -297,6 +299,7 @@ sysprof_time_span_layer_finalize (GObject *object) { SysprofTimeSpanLayer *self = (SysprofTimeSpanLayer *)object; + g_clear_object (&self->series_bindings); g_clear_object (&self->axis); g_clear_object (&self->series); g_clear_object (&self->normal_x); @@ -408,18 +411,8 @@ sysprof_time_span_layer_class_init (SysprofTimeSpanLayerClass *klass) static void sysprof_time_span_layer_init (SysprofTimeSpanLayer *self) { - g_autoptr(GtkExpression) begin_expression = NULL; - g_autoptr(GtkExpression) end_expression = NULL; - - begin_expression = gtk_property_expression_new (SYSPROF_TYPE_TIME_SERIES_ITEM, NULL, "time"); - end_expression = gtk_property_expression_new (SYSPROF_TYPE_TIME_SERIES_ITEM, NULL, "end-time"); - - self->normal_x = g_object_new (SYSPROF_TYPE_NORMALIZED_SERIES, - "expression", begin_expression, - NULL); - self->normal_x2 = g_object_new (SYSPROF_TYPE_NORMALIZED_SERIES, - "expression", end_expression, - NULL); + self->normal_x = g_object_new (SYSPROF_TYPE_NORMALIZED_SERIES, NULL); + self->normal_x2 = g_object_new (SYSPROF_TYPE_NORMALIZED_SERIES, NULL); g_signal_connect_object (self->normal_x, "items-changed", @@ -431,6 +424,14 @@ sysprof_time_span_layer_init (SysprofTimeSpanLayer *self) G_CALLBACK (gtk_widget_queue_draw), self, G_CONNECT_SWAPPED); + + self->series_bindings = g_binding_group_new (); + g_binding_group_bind (self->series_bindings, "time-expression", + self->normal_x, "expression", + G_BINDING_SYNC_CREATE); + g_binding_group_bind (self->series_bindings, "duration-expression", + self->normal_x2, "expression", + G_BINDING_SYNC_CREATE); } const GdkRGBA * @@ -512,6 +513,8 @@ sysprof_time_span_layer_set_series (SysprofTimeSpanLayer *self, if (!g_set_object (&self->series, series)) return; + g_binding_group_set_source (self->series_bindings, series); + sysprof_normalized_series_set_series (self->normal_x, SYSPROF_SERIES (series)); sysprof_normalized_series_set_series (self->normal_x2, SYSPROF_SERIES (series)); From 904815f4f64abb83134ba3a372cb06a8afbda070 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 6 Jul 2023 15:52:41 -0700 Subject: [PATCH 0730/1030] libsysprof-gtk: add test to list processes time duration --- src/libsysprof-gtk/tests/meson.build | 1 + src/libsysprof-gtk/tests/test-processes.c | 350 +++++++++++++++++++ src/libsysprof-gtk/tests/test-processes.ui | 90 +++++ src/libsysprof-gtk/tests/tests.gresource.xml | 1 + 4 files changed, 442 insertions(+) create mode 100644 src/libsysprof-gtk/tests/test-processes.c create mode 100644 src/libsysprof-gtk/tests/test-processes.ui diff --git a/src/libsysprof-gtk/tests/meson.build b/src/libsysprof-gtk/tests/meson.build index 20e8a988..7b488f04 100644 --- a/src/libsysprof-gtk/tests/meson.build +++ b/src/libsysprof-gtk/tests/meson.build @@ -14,6 +14,7 @@ libsysprof_gtk_testsuite_c_args = [ libsysprof_gtk_testsuite = { 'test-callgraph' : {'skip': true}, 'test-charts' : {'skip': true}, + 'test-processes' : {'skip': true}, 'test-tracks' : {'skip': true}, 'test-mark-chart' : {'skip': true}, 'test-mark-table' : {'skip': true}, diff --git a/src/libsysprof-gtk/tests/test-processes.c b/src/libsysprof-gtk/tests/test-processes.c new file mode 100644 index 00000000..fb3ab316 --- /dev/null +++ b/src/libsysprof-gtk/tests/test-processes.c @@ -0,0 +1,350 @@ +/* test-processes.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "tests-resources.h" + +#include +#include +#include + +#include +#include + +static GMainLoop *main_loop; +static char *filename; +static const GOptionEntry entries[] = { + { 0 } +}; + +#define TEST_TYPE_SINGLE_MODEL (test_single_model_get_type()) +G_DECLARE_FINAL_TYPE (TestSingleModel, test_single_model, TEST, SINGLE_MODEL, GObject) + +struct _TestSingleModel +{ + GObject parent_instance; + GObject *item; +}; + +enum { + MODEL_PROP_0, + MODEL_PROP_ITEM, + MODEL_N_PROPS +}; + +static GParamSpec *model_properties[MODEL_N_PROPS]; + +static guint +get_n_items (GListModel *model) +{ + return TEST_SINGLE_MODEL (model)->item ? 1 : 0; +} + +static gpointer +get_item (GListModel *model, + guint position) +{ + if (position == 0 && TEST_SINGLE_MODEL (model)->item) + return g_object_ref (TEST_SINGLE_MODEL (model)->item); + return NULL; +} + +static GType +get_item_type (GListModel *model) +{ + if (TEST_SINGLE_MODEL (model)->item) + return G_OBJECT_TYPE (TEST_SINGLE_MODEL (model)->item); + return G_TYPE_OBJECT; +} + +static void +list_model_iface_init (GListModelInterface *iface) +{ + iface->get_n_items = get_n_items; + iface->get_item = get_item; + iface->get_item_type = get_item_type; +} + +G_DEFINE_FINAL_TYPE_WITH_CODE (TestSingleModel, test_single_model, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init)) + +static void +test_single_model_finalize (GObject *object) +{ + TestSingleModel *self = (TestSingleModel *)object; + + g_clear_object (&self->item); + + G_OBJECT_CLASS (test_single_model_parent_class)->finalize (object); +} + +static void +test_single_model_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + TestSingleModel *self = TEST_SINGLE_MODEL (object); + + switch (prop_id) + { + case MODEL_PROP_ITEM: + g_value_set_object (value, self->item); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +test_single_model_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + TestSingleModel *self = TEST_SINGLE_MODEL (object); + + switch (prop_id) + { + case MODEL_PROP_ITEM: + if (self->item != NULL) + { + g_clear_object (&self->item); + g_list_model_items_changed (G_LIST_MODEL (self), 0, 1, 0); + } + self->item = g_value_dup_object (value); + if (self->item != NULL) + g_list_model_items_changed (G_LIST_MODEL (self), 0, 0, 1); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +test_single_model_class_init (TestSingleModelClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = test_single_model_finalize; + object_class->get_property = test_single_model_get_property; + object_class->set_property = test_single_model_set_property; + + model_properties[MODEL_PROP_ITEM] = + g_param_spec_object ("item", NULL, NULL, + G_TYPE_OBJECT, + (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, MODEL_N_PROPS, model_properties); +} + +static void +test_single_model_init (TestSingleModel *self) +{ +} + +#define TEST_TYPE_PROCESSES (test_processes_get_type()) +G_DECLARE_FINAL_TYPE (TestProcesses, test_processes, TEST, PROCESSES, AdwWindow) + +struct _TestProcesses +{ + AdwWindow parent_instance; + + SysprofDocument *document; + SysprofSession *session; +}; + +G_DEFINE_FINAL_TYPE (TestProcesses, test_processes, ADW_TYPE_WINDOW) + +enum { + PROP_0, + PROP_DOCUMENT, + PROP_SESSION, + N_PROPS +}; + +static GParamSpec *properties [N_PROPS]; + +static void +test_processes_set_document (TestProcesses *self, + SysprofDocument *document) +{ + if (g_set_object (&self->document, document)) + { + g_clear_object (&self->session); + + self->session = sysprof_session_new (self->document); + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DOCUMENT]); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SESSION]); + } +} + +static void +test_processes_dispose (GObject *object) +{ + TestProcesses *self = (TestProcesses *)object; + + g_clear_object (&self->document); + g_clear_object (&self->session); + + G_OBJECT_CLASS (test_processes_parent_class)->dispose (object); +} + +static void +test_processes_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + TestProcesses *self = TEST_PROCESSES (object); + + switch (prop_id) + { + case PROP_DOCUMENT: + g_value_set_object (value, self->document); + break; + + case PROP_SESSION: + g_value_set_object (value, self->session); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +test_processes_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + TestProcesses *self = TEST_PROCESSES (object); + + switch (prop_id) + { + case PROP_DOCUMENT: + test_processes_set_document (self, g_value_get_object (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +test_processes_class_init (TestProcessesClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->dispose = test_processes_dispose; + object_class->get_property = test_processes_get_property; + object_class->set_property = test_processes_set_property; + + properties [PROP_DOCUMENT] = + g_param_spec_object ("document", NULL, NULL, + SYSPROF_TYPE_DOCUMENT, + (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + properties [PROP_SESSION] = + g_param_spec_object ("session", NULL, NULL, + SYSPROF_TYPE_SESSION, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); + + gtk_widget_class_set_template_from_resource (widget_class, "/test-processes.ui"); + + g_type_ensure (TEST_TYPE_SINGLE_MODEL); + g_type_ensure (SYSPROF_TYPE_CHART); + g_type_ensure (SYSPROF_TYPE_CHART_LAYER); + g_type_ensure (SYSPROF_TYPE_COLUMN_LAYER); + g_type_ensure (SYSPROF_TYPE_LINE_LAYER); + g_type_ensure (SYSPROF_TYPE_VALUE_AXIS); + g_type_ensure (SYSPROF_TYPE_SESSION_MODEL); + g_type_ensure (SYSPROF_TYPE_SESSION_MODEL_ITEM); + g_type_ensure (SYSPROF_TYPE_TIME_RULER); + g_type_ensure (SYSPROF_TYPE_TIME_SPAN_LAYER); + g_type_ensure (SYSPROF_TYPE_TIME_SERIES); +} + +static void +test_processes_init (TestProcesses *self) +{ + gtk_widget_init_template (GTK_WIDGET (self)); +} + +int +main (int argc, + char *argv[]) +{ + g_autoptr(GOptionContext) context = g_option_context_new ("- test processes listing"); + g_autoptr(SysprofDocumentLoader) loader = NULL; + g_autoptr(SysprofDocument) document = NULL; + g_autoptr(GError) error = NULL; + GtkWindow *window; + + sysprof_clock_init (); + + gtk_init (); + adw_init (); + + g_option_context_add_main_entries (context, entries, NULL); + if (!g_option_context_parse (context, &argc, &argv, &error)) + { + g_printerr ("%s\n", error->message); + return 1; + } + + if (argc != 2) + { + g_print ("usage: %s [OPTIONS] CAPTURE_FILE\n", argv[0]); + return 1; + } + + g_resources_register (tests_get_resource ()); + + filename = argv[1]; + + main_loop = g_main_loop_new (NULL, FALSE); + + loader = sysprof_document_loader_new (filename); + if (!(document = sysprof_document_loader_load (loader, NULL, &error))) + g_error ("Failed to load document: %s", error->message); + + window = g_object_new (TEST_TYPE_PROCESSES, + "default-width", 800, + "default-height", 600, + "document", document, + NULL); + g_signal_connect_swapped (window, + "close-request", + G_CALLBACK (g_main_loop_quit), + main_loop); + gtk_window_present (window); + g_main_loop_run (main_loop); + + return 0; +} diff --git a/src/libsysprof-gtk/tests/test-processes.ui b/src/libsysprof-gtk/tests/test-processes.ui new file mode 100644 index 00000000..4181861c --- /dev/null +++ b/src/libsysprof-gtk/tests/test-processes.ui @@ -0,0 +1,90 @@ + + + + diff --git a/src/libsysprof-gtk/tests/tests.gresource.xml b/src/libsysprof-gtk/tests/tests.gresource.xml index a3af5faf..fc414536 100644 --- a/src/libsysprof-gtk/tests/tests.gresource.xml +++ b/src/libsysprof-gtk/tests/tests.gresource.xml @@ -2,6 +2,7 @@ test-charts.ui + test-processes.ui test-tracks.ui From f772b82f669d4365bb8b9a9c4f19713bfc6eb108 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 6 Jul 2023 15:53:48 -0700 Subject: [PATCH 0731/1030] libsysprof-analyze: expose files as a property --- src/libsysprof-analyze/sysprof-document.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index 50327706..06f5a29c 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -107,6 +107,7 @@ enum { PROP_0, PROP_ALLOCATIONS, PROP_COUNTERS, + PROP_FILES, PROP_PROCESSES, PROP_SAMPLES, PROP_TIME_SPAN, @@ -314,6 +315,10 @@ sysprof_document_get_property (GObject *object, g_value_take_object (value, sysprof_document_list_counters (self)); break; + case PROP_FILES: + g_value_take_object (value, sysprof_document_list_files (self)); + break; + case PROP_PROCESSES: g_value_take_object (value, sysprof_document_list_processes (self)); break; @@ -353,6 +358,11 @@ sysprof_document_class_init (SysprofDocumentClass *klass) G_TYPE_LIST_MODEL, (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + properties [PROP_FILES] = + g_param_spec_object ("files", NULL, NULL, + G_TYPE_LIST_MODEL, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + properties [PROP_PROCESSES] = g_param_spec_object ("processes", NULL, NULL, G_TYPE_LIST_MODEL, From 6197ad2082473d89c649b2ed168cb72c25cc698e Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 6 Jul 2023 16:01:48 -0700 Subject: [PATCH 0732/1030] libsysprof-gtk: add test to list files in UI --- src/libsysprof-gtk/tests/meson.build | 1 + src/libsysprof-gtk/tests/test-files.c | 208 +++++++++++++++++++ src/libsysprof-gtk/tests/test-files.ui | 56 +++++ src/libsysprof-gtk/tests/tests.gresource.xml | 1 + 4 files changed, 266 insertions(+) create mode 100644 src/libsysprof-gtk/tests/test-files.c create mode 100644 src/libsysprof-gtk/tests/test-files.ui diff --git a/src/libsysprof-gtk/tests/meson.build b/src/libsysprof-gtk/tests/meson.build index 7b488f04..106f9b0e 100644 --- a/src/libsysprof-gtk/tests/meson.build +++ b/src/libsysprof-gtk/tests/meson.build @@ -14,6 +14,7 @@ libsysprof_gtk_testsuite_c_args = [ libsysprof_gtk_testsuite = { 'test-callgraph' : {'skip': true}, 'test-charts' : {'skip': true}, + 'test-files' : {'skip': true}, 'test-processes' : {'skip': true}, 'test-tracks' : {'skip': true}, 'test-mark-chart' : {'skip': true}, diff --git a/src/libsysprof-gtk/tests/test-files.c b/src/libsysprof-gtk/tests/test-files.c new file mode 100644 index 00000000..b83c79ce --- /dev/null +++ b/src/libsysprof-gtk/tests/test-files.c @@ -0,0 +1,208 @@ +/* test-files.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include +#include +#include + +#include +#include + +static GMainLoop *main_loop; +static char *filename; +static const GOptionEntry entries[] = { + { 0 } +}; + +#define TEST_TYPE_FILES (test_files_get_type()) +G_DECLARE_FINAL_TYPE (TestFiles, test_files, TEST, FILES, AdwWindow) + +struct _TestFiles +{ + AdwWindow parent_instance; + + SysprofDocument *document; + SysprofSession *session; +}; + +G_DEFINE_FINAL_TYPE (TestFiles, test_files, ADW_TYPE_WINDOW) + +enum { + PROP_0, + PROP_DOCUMENT, + PROP_SESSION, + N_PROPS +}; + +static GParamSpec *properties [N_PROPS]; + +static void +test_files_set_document (TestFiles *self, + SysprofDocument *document) +{ + if (g_set_object (&self->document, document)) + { + g_clear_object (&self->session); + + self->session = sysprof_session_new (self->document); + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DOCUMENT]); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SESSION]); + } +} + +static void +test_files_dispose (GObject *object) +{ + TestFiles *self = (TestFiles *)object; + + g_clear_object (&self->document); + g_clear_object (&self->session); + + G_OBJECT_CLASS (test_files_parent_class)->dispose (object); +} + +static void +test_files_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + TestFiles *self = TEST_FILES (object); + + switch (prop_id) + { + case PROP_DOCUMENT: + g_value_set_object (value, self->document); + break; + + case PROP_SESSION: + g_value_set_object (value, self->session); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +test_files_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + TestFiles *self = TEST_FILES (object); + + switch (prop_id) + { + case PROP_DOCUMENT: + test_files_set_document (self, g_value_get_object (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +test_files_class_init (TestFilesClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->dispose = test_files_dispose; + object_class->get_property = test_files_get_property; + object_class->set_property = test_files_set_property; + + properties [PROP_DOCUMENT] = + g_param_spec_object ("document", NULL, NULL, + SYSPROF_TYPE_DOCUMENT, + (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + properties [PROP_SESSION] = + g_param_spec_object ("session", NULL, NULL, + SYSPROF_TYPE_SESSION, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); + + gtk_widget_class_set_template_from_resource (widget_class, "/test-files.ui"); + + g_type_ensure (SYSPROF_TYPE_DOCUMENT_FILE); +} + +static void +test_files_init (TestFiles *self) +{ + gtk_widget_init_template (GTK_WIDGET (self)); +} + +int +main (int argc, + char *argv[]) +{ + g_autoptr(GOptionContext) context = g_option_context_new ("- test various files"); + g_autoptr(SysprofDocumentLoader) loader = NULL; + g_autoptr(SysprofDocument) document = NULL; + g_autoptr(GError) error = NULL; + GtkWindow *window; + + sysprof_clock_init (); + + gtk_init (); + adw_init (); + + g_option_context_add_main_entries (context, entries, NULL); + if (!g_option_context_parse (context, &argc, &argv, &error)) + { + g_printerr ("%s\n", error->message); + return 1; + } + + if (argc != 2) + { + g_print ("usage: %s [OPTIONS] CAPTURE_FILE\n", argv[0]); + return 1; + } + + filename = argv[1]; + + main_loop = g_main_loop_new (NULL, FALSE); + + loader = sysprof_document_loader_new (filename); + if (!(document = sysprof_document_loader_load (loader, NULL, &error))) + g_error ("Failed to load document: %s", error->message); + + window = g_object_new (TEST_TYPE_FILES, + "default-width", 800, + "default-height", 600, + "document", document, + NULL); + g_signal_connect_swapped (window, + "close-request", + G_CALLBACK (g_main_loop_quit), + main_loop); + gtk_window_present (window); + g_main_loop_run (main_loop); + + return 0; +} diff --git a/src/libsysprof-gtk/tests/test-files.ui b/src/libsysprof-gtk/tests/test-files.ui new file mode 100644 index 00000000..b85666fc --- /dev/null +++ b/src/libsysprof-gtk/tests/test-files.ui @@ -0,0 +1,56 @@ + + + + diff --git a/src/libsysprof-gtk/tests/tests.gresource.xml b/src/libsysprof-gtk/tests/tests.gresource.xml index fc414536..d3f1eb91 100644 --- a/src/libsysprof-gtk/tests/tests.gresource.xml +++ b/src/libsysprof-gtk/tests/tests.gresource.xml @@ -2,6 +2,7 @@ test-charts.ui + test-files.ui test-processes.ui test-tracks.ui From d993bf3d37be81c74ea775004cff4e477230efca Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 6 Jul 2023 16:53:38 -0700 Subject: [PATCH 0733/1030] libsysprof-analyze: expose logs as document property # Conflicts: # src/libsysprof-analyze/sysprof-document.c --- src/libsysprof-analyze/sysprof-document.c | 31 +++++++++++++++++++++++ src/libsysprof-analyze/sysprof-document.h | 2 ++ 2 files changed, 33 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index 06f5a29c..ea050aa8 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -76,6 +76,7 @@ struct _SysprofDocument EggBitset *samples_with_context_switch; EggBitset *traceables; EggBitset *processes; + EggBitset *logs; EggBitset *mmaps; EggBitset *overlays; EggBitset *pids; @@ -108,6 +109,7 @@ enum { PROP_ALLOCATIONS, PROP_COUNTERS, PROP_FILES, + PROP_LOGS, PROP_PROCESSES, PROP_SAMPLES, PROP_TIME_SPAN, @@ -276,6 +278,7 @@ sysprof_document_finalize (GObject *object) g_clear_pointer (&self->marks, egg_bitset_unref); g_clear_pointer (&self->file_chunks, egg_bitset_unref); g_clear_pointer (&self->jitmaps, egg_bitset_unref); + g_clear_pointer (&self->logs, egg_bitset_unref); g_clear_pointer (&self->mmaps, egg_bitset_unref); g_clear_pointer (&self->overlays, egg_bitset_unref); g_clear_pointer (&self->pids, egg_bitset_unref); @@ -319,6 +322,10 @@ sysprof_document_get_property (GObject *object, g_value_take_object (value, sysprof_document_list_files (self)); break; + case PROP_LOGS: + g_value_take_object (value, sysprof_document_list_logs (self)); + break; + case PROP_PROCESSES: g_value_take_object (value, sysprof_document_list_processes (self)); break; @@ -363,6 +370,11 @@ sysprof_document_class_init (SysprofDocumentClass *klass) G_TYPE_LIST_MODEL, (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + properties [PROP_LOGS] = + g_param_spec_object ("logs", NULL, NULL, + G_TYPE_LIST_MODEL, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + properties [PROP_PROCESSES] = g_param_spec_object ("processes", NULL, NULL, G_TYPE_LIST_MODEL, @@ -403,6 +415,7 @@ sysprof_document_init (SysprofDocument *self) self->marks = egg_bitset_new_empty (); self->file_chunks = egg_bitset_new_empty (); self->jitmaps = egg_bitset_new_empty (); + self->logs = egg_bitset_new_empty (); self->mmaps = egg_bitset_new_empty (); self->overlays = egg_bitset_new_empty (); self->pids = egg_bitset_new_empty (); @@ -1059,6 +1072,10 @@ sysprof_document_load_worker (GTask *task, egg_bitset_add (self->jitmaps, f); break; + case SYSPROF_CAPTURE_FRAME_LOG: + egg_bitset_add (self->logs, f); + break; + case SYSPROF_CAPTURE_FRAME_MAP: egg_bitset_add (self->mmaps, f); break; @@ -1720,6 +1737,20 @@ finish: return n_symbolized; } +/** + * sysprof_document_list_logs: + * @self: a #SysprofDocument + * + * Returns: (transfer full): a #GListModel of #SysprofDocumentLog + */ +GListModel * +sysprof_document_list_logs (SysprofDocument *self) +{ + g_return_val_if_fail (SYSPROF_IS_DOCUMENT (self), NULL); + + return _sysprof_document_bitset_index_new (G_LIST_MODEL (self), self->logs); +} + /** * sysprof_document_list_counters: * @self: a #SysprofDocument diff --git a/src/libsysprof-analyze/sysprof-document.h b/src/libsysprof-analyze/sysprof-document.h index 233a0d5a..d55243b6 100644 --- a/src/libsysprof-analyze/sysprof-document.h +++ b/src/libsysprof-analyze/sysprof-document.h @@ -53,6 +53,8 @@ GListModel *sysprof_document_list_traceables (SysprofDocume SYSPROF_AVAILABLE_IN_ALL GListModel *sysprof_document_list_allocations (SysprofDocument *self); SYSPROF_AVAILABLE_IN_ALL +GListModel *sysprof_document_list_logs (SysprofDocument *self); +SYSPROF_AVAILABLE_IN_ALL GListModel *sysprof_document_list_samples (SysprofDocument *self); SYSPROF_AVAILABLE_IN_ALL GListModel *sysprof_document_list_samples_with_context_switch From fc10c98e8d240801b0e93e598c44ae80a6021e4c Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 7 Jul 2023 13:01:07 -0700 Subject: [PATCH 0734/1030] libsysprof-gtk: include sysprof-analyze.h --- src/libsysprof-gtk/sysprof-gtk.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libsysprof-gtk/sysprof-gtk.h b/src/libsysprof-gtk/sysprof-gtk.h index d8a4488b..33cc64d1 100644 --- a/src/libsysprof-gtk/sysprof-gtk.h +++ b/src/libsysprof-gtk/sysprof-gtk.h @@ -20,6 +20,8 @@ #pragma once +#include + G_BEGIN_DECLS #define SYSPROF_GTK_INSIDE @@ -52,4 +54,3 @@ G_BEGIN_DECLS #undef SYSPROF_GTK_INSIDE G_END_DECLS - From d6539c1bdb88585aa360441975b51bc6f29fb55d Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 7 Jul 2023 13:01:15 -0700 Subject: [PATCH 0735/1030] sysprof: start on new application design --- src/sysprof/gtk/help-overlay.ui | 108 +------- src/sysprof/gtk/menus.ui | 46 +--- src/sysprof/{sysprof.c => main.c} | 14 +- src/sysprof/meson.build | 12 +- src/sysprof/sysprof-application.c | 150 ++--------- src/sysprof/sysprof-window.c | 423 +++++++++++++----------------- src/sysprof/sysprof-window.h | 16 +- src/sysprof/sysprof-window.ui | 191 ++++++++++---- src/sysprof/sysprof.gresource.xml | 6 +- src/sysprof/theme/shared.css | 12 - 10 files changed, 383 insertions(+), 595 deletions(-) rename src/sysprof/{sysprof.c => main.c} (86%) delete mode 100644 src/sysprof/theme/shared.css diff --git a/src/sysprof/gtk/help-overlay.ui b/src/sysprof/gtk/help-overlay.ui index 69db015c..8c0b11ad 100644 --- a/src/sysprof/gtk/help-overlay.ui +++ b/src/sysprof/gtk/help-overlay.ui @@ -5,99 +5,13 @@ shortcuts 12 - - - Files - - - Save Recording - Saves the current recording - win.save-capture - - - - - Open recording - Opens a previously saved recording - app.open-capture - - - - - - - Recording - - - Record again - Starts a new recording - win.replay-capture - - - - - Stop recording - Escape - - - - - - - Callgraph - - - Expand function - Shows the direct descendants of the callgraph function - Right - - - - - Collapse function - Hides all callgraph descendants below the selected function - Left - - - - - Jump into function - Selects the function or file as the top of the callgraph - Return - - - - - - - Visualizers - - - Zoom in - <primary>plus - - - - - Zoom out - <primary>minus - - - - - Reset zoom - <primary>0 - - - - General Show Help - app.help + win.help @@ -106,28 +20,10 @@ win.show-help-overlay - - - New Tab - win.new-tab - - - - - Switch Tab - <alt>1...9 - - - - - New Window - app.new-window - - Close Window - win.close-tab + window.close diff --git a/src/sysprof/gtk/menus.ui b/src/sysprof/gtk/menus.ui index ea3d9ca0..335463c5 100644 --- a/src/sysprof/gtk/menus.ui +++ b/src/sysprof/gtk/menus.ui @@ -1,50 +1,22 @@ -
- - New Tab - win.new-tab - - - New Window - app.new-window - -
-
- - Open Recording… - app.open-capture - - - Save Recording… - win.save-capture - -
-
- - Record Again - win.replay-capture - -
-
- - Close - win.close-tab - -
- Keyboard Shortcuts + _Preferences + win.preferences + + + _Keyboard Shortcuts win.show-help-overlay - Help - app.help + _Help + win.help - About Sysprof - app.about + _About Sysprof + win.about
diff --git a/src/sysprof/sysprof.c b/src/sysprof/main.c similarity index 86% rename from src/sysprof/sysprof.c rename to src/sysprof/main.c index 7a8d509a..f99cff34 100644 --- a/src/sysprof/sysprof.c +++ b/src/sysprof/main.c @@ -1,6 +1,6 @@ /* main.c * - * Copyright 2016 Christian Hergert + * Copyright 2016-2023 Christian Hergert * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -14,20 +14,24 @@ * * 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 #include "sysprof-application.h" -gint -main (gint argc, - gchar *argv[]) +int +main (int argc, + char *argv[]) { g_autoptr(SysprofApplication) app = NULL; gint ret; diff --git a/src/sysprof/meson.build b/src/sysprof/meson.build index bc5205c7..facc30af 100644 --- a/src/sysprof/meson.build +++ b/src/sysprof/meson.build @@ -1,5 +1,5 @@ sysprof_sources = [ - 'sysprof.c', + 'main.c', 'sysprof-application.c', 'sysprof-window.c', ] @@ -11,11 +11,11 @@ sysprof_resources = gnome.compile_resources('sysprof-resources', 'sysprof.gresou sysprof_deps = [ cc.find_library('m', required: false), - libsysprof_capture_dep, - libsysprof_dep, - libsysprof_ui_dep, - dependency('pangoft2', required: false), - dependency('libadwaita-1', version: '>= 1.2.alpha'), + dependency('libadwaita-1', version: '>= 1.4.alpha'), + dependency('libpanel-1', version: '>= 1.3.0'), + libsysprof_analyze_static_dep, + libsysprof_profile_static_dep, + libsysprof_gtk_static_dep, ] sysprof = executable('sysprof', sysprof_resources + sysprof_sources, diff --git a/src/sysprof/sysprof-application.c b/src/sysprof/sysprof-application.c index 66ce5c25..fda46796 100644 --- a/src/sysprof/sysprof-application.c +++ b/src/sysprof/sysprof-application.c @@ -32,100 +32,37 @@ struct _SysprofApplication G_DEFINE_TYPE (SysprofApplication, sysprof_application, ADW_TYPE_APPLICATION) -struct { - const gchar *action_name; - const gchar *accels[12]; -} default_accels[] = { - { "app.help", { "F1", NULL } }, - { "app.quit", { "q", NULL } }, - { "app.new-window", { "n", NULL } }, - { "app.open-capture", { "o", NULL } }, - { "zoom.zoom-in", { "plus", "KP_Add", "equal", "ZoomIn", NULL } }, - { "zoom.zoom-out", { "minus", "KP_Subtract", "ZoomOut", NULL } }, - { "zoom.zoom-one", { "0", "KP_0", NULL } }, - { "win.new-tab", { "t", NULL } }, - { "win.close-tab", { "w", NULL } }, - { "win.replay-capture", { "r", NULL } }, - { "win.save-capture", { "s", NULL } }, - { "win.switch-tab(1)", { "1", NULL } }, - { "win.switch-tab(2)", { "2", NULL } }, - { "win.switch-tab(3)", { "3", NULL } }, - { "win.switch-tab(4)", { "4", NULL } }, - { "win.switch-tab(5)", { "5", NULL } }, - { "win.switch-tab(6)", { "6", NULL } }, - { "win.switch-tab(7)", { "7", NULL } }, - { "win.switch-tab(8)", { "8", NULL } }, - { "win.switch-tab(9)", { "9", NULL } }, - { NULL } -}; - static void sysprof_application_activate (GApplication *app) { - SysprofWindow *window; - GList *windows; - g_assert (GTK_IS_APPLICATION (app)); - windows = gtk_application_get_windows (GTK_APPLICATION (app)); - - for (; windows != NULL; windows = windows->next) + for (const GList *iter = gtk_application_get_windows (GTK_APPLICATION (app)); + iter != NULL; + iter = iter->next) { - if (SYSPROF_IS_WINDOW (windows->data)) + if (SYSPROF_IS_WINDOW (iter->data)) { - gtk_window_present (windows->data); + gtk_window_present (iter->data); return; } } - window = SYSPROF_WINDOW (sysprof_window_new (SYSPROF_APPLICATION (app))); - gtk_window_present (GTK_WINDOW (window)); + g_warning ("TODO: show greeter window"); } static void sysprof_application_open (GApplication *app, GFile **files, - gint n_files, - const gchar *hint) + int n_files, + const char *hint) { - GtkWidget *window; g_assert (SYSPROF_IS_APPLICATION (app)); g_assert (files != NULL || n_files == 0); - window = sysprof_window_new (SYSPROF_APPLICATION (app)); - - /* Present window before opening files so that message dialogs - * always display above the window. - */ - gtk_window_present (GTK_WINDOW (window)); - - for (gint i = 0; i < n_files; i++) - sysprof_window_open (SYSPROF_WINDOW (window), files[i]); - - if (n_files == 0) - sysprof_application_activate (app); -} - -static void -sysprof_application_startup (GApplication *application) -{ - g_autoptr(GtkCssProvider) provider = NULL; - - g_assert (SYSPROF_IS_APPLICATION (application)); - - G_APPLICATION_CLASS (sysprof_application_parent_class)->startup (application); - - provider = gtk_css_provider_new (); - gtk_css_provider_load_from_resource (provider, "/org/gnome/sysprof/theme/shared.css"); - gtk_style_context_add_provider_for_display (gdk_display_get_default (), - GTK_STYLE_PROVIDER (provider), - GTK_STYLE_PROVIDER_PRIORITY_THEME+1); - - for (guint i = 0; default_accels[i].action_name; i++) - gtk_application_set_accels_for_action (GTK_APPLICATION (application), - default_accels[i].action_name, - default_accels[i].accels); + for (guint i = 0; i < n_files; i++) + sysprof_window_open (SYSPROF_APPLICATION (app), files[i]); } static void @@ -149,7 +86,6 @@ sysprof_application_class_init (SysprofApplicationClass *klass) GtkApplicationClass *gtk_app_class = GTK_APPLICATION_CLASS (klass); app_class->open = sysprof_application_open; - app_class->startup = sysprof_application_startup; app_class->activate = sysprof_application_activate; gtk_app_class->window_added = sysprof_application_window_added; @@ -176,7 +112,7 @@ sysprof_about (GSimpleAction *action, { GtkApplication *app = user_data; GtkWindow *best_toplevel = NULL; - GList *windows; + const GList *windows; g_assert (G_IS_APPLICATION (app)); g_assert (G_IS_SIMPLE_ACTION (action)); @@ -193,12 +129,12 @@ sysprof_about (GSimpleAction *action, } } - adw_show_about_window(best_toplevel, + adw_show_about_window (best_toplevel, "application-name", _("Sysprof"), "application-icon", APP_ID_S, "version", "GNOME " SYMBOLIC_VERSION " (" PACKAGE_VERSION ")", "copyright", "Copyright 2004-2009 Søren Sandmann Pedersen\n" - "Copyright 2016-2021 Christian Hergert", + "Copyright 2016-2023 Christian Hergert", "issue-url", "https://gitlab.gnome.org/GNOME/sysprof/-/issues/new", "license-type", GTK_LICENSE_GPL_3_0, "developers", sysprof_authors, @@ -215,67 +151,24 @@ sysprof_help (GSimpleAction *action, gpointer user_data) { SysprofApplication *self = user_data; + g_autoptr(GtkUriLauncher) launcher = NULL; GtkWindow *window; g_assert (SYSPROF_IS_APPLICATION (self)); g_assert (G_IS_SIMPLE_ACTION (action)); window = gtk_application_get_active_window (GTK_APPLICATION (self)); - - gtk_show_uri (window, "help:sysprof", GDK_CURRENT_TIME); -} - -static void -sysprof_new_window (GSimpleAction *action, - GVariant *variant, - gpointer user_data) -{ - SysprofApplication *self = user_data; - SysprofWindow *window; - - g_assert (SYSPROF_IS_APPLICATION (self)); - g_assert (G_IS_SIMPLE_ACTION (action)); - g_assert (variant == NULL); - - window = SYSPROF_WINDOW (sysprof_window_new (self)); - gtk_window_present (GTK_WINDOW (window)); -} - -static void -sysprof_open_capture (GSimpleAction *action, - GVariant *variant, - gpointer user_data) -{ - GtkApplication *app = user_data; - GList *list; - - g_assert (G_IS_APPLICATION (app)); - g_assert (G_IS_SIMPLE_ACTION (action)); - g_assert (variant == NULL); - - list = gtk_application_get_windows (app); - - for (; list != NULL; list = list->next) - { - GtkWindow *window = list->data; - - if (SYSPROF_IS_WINDOW (window)) - { - sysprof_window_open_from_dialog (SYSPROF_WINDOW (window)); - break; - } - } + launcher = gtk_uri_launcher_new ("help:sysprof"); + gtk_uri_launcher_launch (launcher, window, NULL, NULL, NULL); } static void sysprof_application_init (SysprofApplication *self) { static const GActionEntry actions[] = { - { "about", sysprof_about }, - { "new-window", sysprof_new_window }, - { "open-capture", sysprof_open_capture }, - { "help", sysprof_help }, - { "quit", sysprof_quit }, + { "about", sysprof_about }, + { "help", sysprof_help }, + { "quit", sysprof_quit }, }; setlocale (LC_ALL, ""); @@ -286,7 +179,10 @@ sysprof_application_init (SysprofApplication *self) g_set_application_name (_("Sysprof")); - g_action_map_add_action_entries (G_ACTION_MAP (self), actions, G_N_ELEMENTS (actions), self); + g_action_map_add_action_entries (G_ACTION_MAP (self), + actions, + G_N_ELEMENTS (actions), + self); g_application_set_default (G_APPLICATION (self)); } diff --git a/src/sysprof/sysprof-window.c b/src/sysprof/sysprof-window.c index 2da8b091..11cf6933 100644 --- a/src/sysprof/sysprof-window.c +++ b/src/sysprof/sysprof-window.c @@ -1,6 +1,6 @@ /* sysprof-window.c * - * Copyright 2016-2019 Christian Hergert + * Copyright 2023 Christian Hergert * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,167 +18,99 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -#define G_LOG_DOMAIN "sysprof-window" - #include "config.h" #include -#include + +#include #include "sysprof-window.h" struct _SysprofWindow { AdwApplicationWindow parent_instance; - - GBindingGroup *bindings; - - SysprofNotebook *notebook; - GtkButton *open_button; - GtkMenuButton *menu_button; + SysprofDocument *document; + SysprofSession *session; }; -G_DEFINE_TYPE (SysprofWindow, sysprof_window, ADW_TYPE_APPLICATION_WINDOW) +enum { + PROP_0, + PROP_DOCUMENT, + PROP_SESSION, + N_PROPS +}; -/** - * sysprof_window_new: - * - * Create a new #SysprofWindow. - * - * Returns: (transfer full): a newly created #SysprofWindow - */ -GtkWidget * -sysprof_window_new (SysprofApplication *application) -{ - return g_object_new (SYSPROF_TYPE_WINDOW, - "application", application, - NULL); -} +G_DEFINE_FINAL_TYPE (SysprofWindow, sysprof_window, ADW_TYPE_APPLICATION_WINDOW) + +static GParamSpec *properties [N_PROPS]; static void -sysprof_window_notify_can_replay_cb (SysprofWindow *self, - GParamSpec *pspec, - SysprofNotebook *notebook) +sysprof_window_set_document (SysprofWindow *self, + SysprofDocument *document) { g_assert (SYSPROF_IS_WINDOW (self)); - g_assert (SYSPROF_IS_NOTEBOOK (notebook)); + g_assert (SYSPROF_IS_DOCUMENT (document)); + g_assert (self->document == NULL); + g_assert (self->session == NULL); - gtk_widget_action_set_enabled (GTK_WIDGET (self), - "win.replay-capture", - sysprof_notebook_get_can_replay (notebook)); + g_set_object (&self->document, document); + + self->session = sysprof_session_new (document); } static void -sysprof_window_notify_can_save_cb (SysprofWindow *self, - GParamSpec *pspec, - SysprofNotebook *notebook) -{ - g_assert (SYSPROF_IS_WINDOW (self)); - g_assert (SYSPROF_IS_NOTEBOOK (notebook)); - - gtk_widget_action_set_enabled (GTK_WIDGET (self), - "win.save-capture", - sysprof_notebook_get_can_save (notebook)); -} - -static void -new_tab_cb (GtkWidget *widget, - const char *action_name, - GVariant *param) -{ - SysprofWindow *self = (SysprofWindow *)widget; - - g_return_if_fail (SYSPROF_IS_WINDOW (self)); - - sysprof_window_new_tab (self); -} - -static void -switch_tab_cb (GtkWidget *widget, - const char *action_name, - GVariant *param) -{ - SysprofWindow *self = (SysprofWindow *)widget; - int page; - - g_return_if_fail (SYSPROF_IS_WINDOW (self)); - g_return_if_fail (g_variant_is_of_type (param, G_VARIANT_TYPE_INT32)); - - page = g_variant_get_int32 (param); - sysprof_notebook_set_current_page (self->notebook, page - 1); -} - -static void -close_tab_cb (GtkWidget *widget, - const char *action_name, - GVariant *param) -{ - SysprofWindow *self = (SysprofWindow *)widget; - - g_return_if_fail (SYSPROF_IS_WINDOW (self)); - - if (sysprof_notebook_get_n_pages (self->notebook) == 1) - { - SysprofDisplay *child = sysprof_notebook_get_nth_page (self->notebook, 0); - - if (SYSPROF_IS_DISPLAY (child) && - sysprof_display_is_empty (SYSPROF_DISPLAY (child))) - { - gtk_window_destroy (GTK_WINDOW (self)); - return; - } - } - - sysprof_notebook_close_current (self->notebook); -} - -static void -replay_capture_cb (GtkWidget *widget, - const char *action_name, - GVariant *param) -{ - SysprofWindow *self = (SysprofWindow *)widget; - - g_return_if_fail (SYSPROF_IS_WINDOW (self)); - - sysprof_notebook_replay (self->notebook); -} - -static void -save_capture_cb (GtkWidget *widget, - const char *action_name, - GVariant *param) -{ - SysprofWindow *self = (SysprofWindow *)widget; - - g_return_if_fail (SYSPROF_IS_WINDOW (self)); - - sysprof_notebook_save (self->notebook); -} - -static void -stop_recording_cb (GtkWidget *widget, - const char *action_name, - GVariant *param) -{ - SysprofWindow *self = (SysprofWindow *)widget; - SysprofDisplay *current; - - g_assert (SYSPROF_IS_WINDOW (self)); - - if ((current = sysprof_notebook_get_current (self->notebook))) - sysprof_display_stop_recording (current); -} - -static void -sysprof_window_finalize (GObject *object) +sysprof_window_dispose (GObject *object) { SysprofWindow *self = (SysprofWindow *)object; - g_binding_group_set_source (self->bindings, NULL); - g_clear_object (&self->bindings); + gtk_widget_dispose_template (GTK_WIDGET (self), SYSPROF_TYPE_WINDOW); - G_OBJECT_CLASS (sysprof_window_parent_class)->finalize (object); + g_clear_object (&self->document); + g_clear_object (&self->session); + + G_OBJECT_CLASS (sysprof_window_parent_class)->dispose (object); +} + +static void +sysprof_window_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofWindow *self = SYSPROF_WINDOW (object); + + switch (prop_id) + { + case PROP_DOCUMENT: + g_value_set_object (value, sysprof_window_get_document (self)); + break; + + case PROP_SESSION: + g_value_set_object (value, sysprof_window_get_session (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_window_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SysprofWindow *self = SYSPROF_WINDOW (object); + + switch (prop_id) + { + case PROP_DOCUMENT: + sysprof_window_set_document (self, g_value_get_object (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } } static void @@ -187,131 +119,138 @@ sysprof_window_class_init (SysprofWindowClass *klass) GObjectClass *object_class = G_OBJECT_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); - object_class->finalize = sysprof_window_finalize; + object_class->dispose = sysprof_window_dispose; + object_class->get_property = sysprof_window_get_property; + object_class->set_property = sysprof_window_set_property; - gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/sysprof/ui/sysprof-window.ui"); - gtk_widget_class_bind_template_child (widget_class, SysprofWindow, menu_button); - gtk_widget_class_bind_template_child (widget_class, SysprofWindow, open_button); - gtk_widget_class_bind_template_child (widget_class, SysprofWindow, notebook); + properties[PROP_DOCUMENT] = + g_param_spec_object ("document", NULL, NULL, + SYSPROF_TYPE_DOCUMENT, + (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); - gtk_widget_class_install_action (widget_class, "win.close-tab", NULL, close_tab_cb); - gtk_widget_class_install_action (widget_class, "win.new-tab", NULL, new_tab_cb); - gtk_widget_class_install_action (widget_class, "win.switch-tab", "i", switch_tab_cb); - gtk_widget_class_install_action (widget_class, "win.replay-capture", NULL, replay_capture_cb); - gtk_widget_class_install_action (widget_class, "win.save-capture", NULL, save_capture_cb); - gtk_widget_class_install_action (widget_class, "win.stop-recording", NULL, stop_recording_cb); + properties[PROP_SESSION] = + g_param_spec_object ("session", NULL, NULL, + SYSPROF_TYPE_SESSION, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - gtk_widget_class_add_binding_action (widget_class, GDK_KEY_Escape, 0, "win.stop-recording", NULL); + g_object_class_install_properties (object_class, N_PROPS, properties); - g_type_ensure (SYSPROF_TYPE_NOTEBOOK); - g_type_ensure (SYSPROF_TYPE_DISPLAY); + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/sysprof/sysprof-window.ui"); + + g_type_ensure (SYSPROF_TYPE_DOCUMENT); + g_type_ensure (SYSPROF_TYPE_SESSION); } static void sysprof_window_init (SysprofWindow *self) { - GMenu *menu; - gtk_widget_init_template (GTK_WIDGET (self)); - - menu = gtk_application_get_menu_by_id (GTK_APPLICATION (g_application_get_default ()), "win-menu"); - gtk_menu_button_set_menu_model (self->menu_button, G_MENU_MODEL (menu)); - - g_signal_connect_object (self->notebook, - "notify::can-replay", - G_CALLBACK (sysprof_window_notify_can_replay_cb), - self, - G_CONNECT_SWAPPED); - - g_signal_connect_object (self->notebook, - "notify::can-save", - G_CALLBACK (sysprof_window_notify_can_save_cb), - self, - G_CONNECT_SWAPPED); - - self->bindings = g_binding_group_new (); - g_binding_group_bind (self->bindings, "title", self, "title", G_BINDING_SYNC_CREATE); - g_object_bind_property (self->notebook, "current", self->bindings, "source", - G_BINDING_SYNC_CREATE); - - gtk_widget_action_set_enabled (GTK_WIDGET (self), "win.save-capture", FALSE); - gtk_widget_action_set_enabled (GTK_WIDGET (self), "win.replay-capture", FALSE); } -void -sysprof_window_open (SysprofWindow *self, - GFile *file) +GtkWidget * +sysprof_window_new (SysprofApplication *app, + SysprofDocument *document) { - g_return_if_fail (SYSPROF_IS_WINDOW (self)); - g_return_if_fail (G_IS_FILE (file)); + g_return_val_if_fail (SYSPROF_IS_APPLICATION (app), NULL); + g_return_val_if_fail (SYSPROF_IS_DOCUMENT (document), NULL); - sysprof_notebook_open (self->notebook, file); + return g_object_new (SYSPROF_TYPE_WINDOW, + "application", app, + "document", document, + NULL); +} + +/** + * sysprof_window_get_session: + * @self: a #SysprofWindow + * + * Gets the session for the window. + * + * Returns: (transfer none): a #SysprofSession + */ +SysprofSession * +sysprof_window_get_session (SysprofWindow *self) +{ + g_return_val_if_fail (SYSPROF_IS_WINDOW (self), NULL); + + return self->session; +} + +/** + * sysprof_window_get_document: + * @self: a #SysprofWindow + * + * Gets the document for the window. + * + * Returns: (transfer none): a #SysprofDocument + */ +SysprofDocument * +sysprof_window_get_document (SysprofWindow *self) +{ + g_return_val_if_fail (SYSPROF_IS_WINDOW (self), NULL); + + return self->document; } static void -sysprof_window_open_from_dialog_cb (SysprofWindow *self, - int response, - GtkFileChooserNative *dialog) +sysprof_window_load_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) { - g_assert (SYSPROF_IS_WINDOW (self)); - g_assert (GTK_IS_FILE_CHOOSER_NATIVE (dialog)); + SysprofDocumentLoader *loader = (SysprofDocumentLoader *)object; + g_autoptr(SysprofApplication) app = user_data; + g_autoptr(SysprofDocument) document = NULL; + g_autoptr(GError) error = NULL; - if (response == GTK_RESPONSE_ACCEPT) + g_assert (SYSPROF_IS_DOCUMENT_LOADER (loader)); + g_assert (G_IS_ASYNC_RESULT (result)); + g_assert (SYSPROF_IS_APPLICATION (app)); + + g_application_release (G_APPLICATION (app)); + + if (!(document = sysprof_document_loader_load_finish (loader, result, &error))) { - g_autoptr(GFile) file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog)); + GtkWidget *dialog; - if (g_file_is_native (file)) - sysprof_window_open (self, file); + dialog = adw_message_dialog_new (NULL, _("Invalid Document"), NULL); + adw_message_dialog_format_body (ADW_MESSAGE_DIALOG (dialog), + _("The document could not be loaded. Please check that you have the correct capture file.\n\n%s"), + error->message); + adw_message_dialog_add_response (ADW_MESSAGE_DIALOG (dialog), "close", _("Close")); + gtk_application_add_window (GTK_APPLICATION (app), GTK_WINDOW (dialog)); + gtk_window_present (GTK_WINDOW (dialog)); + } + else + { + GtkWidget *window; + + window = sysprof_window_new (app, document); + gtk_window_present (GTK_WINDOW (window)); + } +} + +void +sysprof_window_open (SysprofApplication *app, + GFile *file) +{ + g_autoptr(SysprofDocumentLoader) loader = NULL; + + g_return_if_fail (SYSPROF_IS_APPLICATION (app)); + g_return_if_fail (G_IS_FILE (file)); + + if (!g_file_is_native (file)) + { + g_autofree char *uri = g_file_get_uri (file); + g_warning ("Cannot open non-native file \"%s\"", uri); + return; } - gtk_native_dialog_destroy (GTK_NATIVE_DIALOG (dialog)); -} - -void -sysprof_window_open_from_dialog (SysprofWindow *self) -{ - GtkFileChooserNative *dialog; - GtkFileFilter *filter; - - g_return_if_fail (SYSPROF_IS_WINDOW (self)); - - /* Translators: This is a window title. */ - dialog = gtk_file_chooser_native_new (_("Open Capture…"), - GTK_WINDOW (self), - GTK_FILE_CHOOSER_ACTION_OPEN, - /* Translators: This is a button. */ - _("Open"), - /* Translators: This is a button. */ - _("Cancel")); - - filter = gtk_file_filter_new (); - gtk_file_filter_set_name (filter, _("Sysprof Captures")); - gtk_file_filter_add_pattern (filter, "*.syscap"); - gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter); - - filter = gtk_file_filter_new (); - gtk_file_filter_set_name (filter, _("All Files")); - gtk_file_filter_add_pattern (filter, "*"); - gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter); - - g_signal_connect_object (dialog, - "response", - G_CALLBACK (sysprof_window_open_from_dialog_cb), - self, - G_CONNECT_SWAPPED); - - gtk_native_dialog_show (GTK_NATIVE_DIALOG (dialog)); -} - -void -sysprof_window_new_tab (SysprofWindow *self) -{ - GtkWidget *display; - gint page; - - g_return_if_fail (SYSPROF_IS_WINDOW (self)); - - display = sysprof_display_new (); - page = sysprof_notebook_append (self->notebook, SYSPROF_DISPLAY (display)); - sysprof_notebook_set_current_page (self->notebook, page); + g_application_hold (G_APPLICATION (app)); + + loader = sysprof_document_loader_new (g_file_peek_path (file)); + sysprof_document_loader_load_async (loader, + NULL, + sysprof_window_load_cb, + g_object_ref (app)); + } diff --git a/src/sysprof/sysprof-window.h b/src/sysprof/sysprof-window.h index e0ca3ce5..6b1bf06c 100644 --- a/src/sysprof/sysprof-window.h +++ b/src/sysprof/sysprof-window.h @@ -1,6 +1,6 @@ /* sysprof-window.h * - * Copyright 2016-2019 Christian Hergert + * Copyright 2023 Christian Hergert * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,6 +22,9 @@ #include +#include +#include + #include "sysprof-application.h" G_BEGIN_DECLS @@ -30,10 +33,11 @@ G_BEGIN_DECLS G_DECLARE_FINAL_TYPE (SysprofWindow, sysprof_window, SYSPROF, WINDOW, AdwApplicationWindow) -GtkWidget *sysprof_window_new (SysprofApplication *application); -void sysprof_window_new_tab (SysprofWindow *self); -void sysprof_window_open (SysprofWindow *self, - GFile *file); -void sysprof_window_open_from_dialog (SysprofWindow *self); +GtkWidget *sysprof_window_new (SysprofApplication *app, + SysprofDocument *document); +void sysprof_window_open (SysprofApplication *app, + GFile *file); +SysprofDocument *sysprof_window_get_document (SysprofWindow *self); +SysprofSession *sysprof_window_get_session (SysprofWindow *self); G_END_DECLS diff --git a/src/sysprof/sysprof-window.ui b/src/sysprof/sysprof-window.ui index 046cd194..71ac6688 100644 --- a/src/sysprof/sysprof-window.ui +++ b/src/sysprof/sysprof-window.ui @@ -1,59 +1,152 @@ - + + + + + +
+ + Open Recording… + + + Save As… + +
+
+ + Record Again… + +
+
+ + Show Embedded Files… + + + Show Embedded Metadata… + +
+
+ + Preferences + app.preferences + <ctrl>comma + + + Help + F1 + app.help + + + About Sysprof + app.about + +
+
diff --git a/src/sysprof/sysprof.gresource.xml b/src/sysprof/sysprof.gresource.xml index faee1ac4..85ebf08b 100644 --- a/src/sysprof/sysprof.gresource.xml +++ b/src/sysprof/sysprof.gresource.xml @@ -1,15 +1,11 @@ - gtk/help-overlay.ui gtk/menus.ui - - - theme/shared.css - + sysprof-window.ui diff --git a/src/sysprof/theme/shared.css b/src/sysprof/theme/shared.css deleted file mode 100644 index 878dcb69..00000000 --- a/src/sysprof/theme/shared.css +++ /dev/null @@ -1,12 +0,0 @@ -visualizers list { - background: @theme_bg_color; -} - -visualizers list row:backdrop, -visualizers list row:last-child { - border-bottom: none; -} - -ticks { - color: alpha(@theme_fg_color, 0.4); -} From 33e03b4458f075e425f3d64ca92eb139d012da5d Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 7 Jul 2023 13:41:28 -0700 Subject: [PATCH 0736/1030] sysprof: add default app macro --- src/sysprof/sysprof-application.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sysprof/sysprof-application.h b/src/sysprof/sysprof-application.h index 4c825395..28d41c9b 100644 --- a/src/sysprof/sysprof-application.h +++ b/src/sysprof/sysprof-application.h @@ -22,7 +22,8 @@ G_BEGIN_DECLS -#define SYSPROF_TYPE_APPLICATION (sysprof_application_get_type()) +#define SYSPROF_TYPE_APPLICATION (sysprof_application_get_type()) +#define SYSPROF_APPLICATION_DEFAULT (SYSPROF_APPLICATION(g_application_get_default())) G_DECLARE_FINAL_TYPE (SysprofApplication, sysprof_application, SYSPROF, APPLICATION, AdwApplication) From 24145578d35af3af53eb6af1d719853b816ccc1a Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 7 Jul 2023 13:41:39 -0700 Subject: [PATCH 0737/1030] sysprof: implement open action --- src/sysprof/sysprof-window.c | 51 +++++++++++++++++++++++++++++++++++ src/sysprof/sysprof-window.ui | 11 ++++++++ 2 files changed, 62 insertions(+) diff --git a/src/sysprof/sysprof-window.c b/src/sysprof/sysprof-window.c index 11cf6933..bb0c2b49 100644 --- a/src/sysprof/sysprof-window.c +++ b/src/sysprof/sysprof-window.c @@ -44,6 +44,55 @@ G_DEFINE_FINAL_TYPE (SysprofWindow, sysprof_window, ADW_TYPE_APPLICATION_WINDOW) static GParamSpec *properties [N_PROPS]; +static void +sysprof_window_open_capture_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + GtkFileDialog *dialog = (GtkFileDialog *)object; + g_autoptr(SysprofWindow) self = user_data; + g_autoptr(GError) error = NULL; + g_autoptr(GFile) file = NULL; + + g_assert (GTK_IS_FILE_DIALOG (dialog)); + g_assert (G_IS_ASYNC_RESULT (result)); + g_assert (SYSPROF_IS_WINDOW (self)); + + if ((file = gtk_file_dialog_open_finish (dialog, result, &error))) + sysprof_window_open (SYSPROF_APPLICATION_DEFAULT, file); +} + +static void +sysprof_window_open_capture_action (GtkWidget *widget, + const char *action_name, + GVariant *param) +{ + g_autoptr(GtkFileDialog) dialog = NULL; + g_autoptr(GtkFileFilter) filter = NULL; + g_autoptr(GListStore) filters = NULL; + + g_assert (SYSPROF_IS_WINDOW (widget)); + + filter = gtk_file_filter_new (); + gtk_file_filter_add_pattern (filter, "*.syscap"); + gtk_file_filter_add_mime_type (filter, "application/x-sysprof-capture"); + gtk_file_filter_set_name (filter, _("Sysprof Capture (*.syscap)")); + + filters = g_list_store_new (GTK_TYPE_FILE_FILTER); + g_list_store_append (filters, filter); + + dialog = gtk_file_dialog_new (); + gtk_file_dialog_set_title (dialog, _("Open Recording")); + gtk_file_dialog_set_filters (dialog, G_LIST_MODEL (filters)); + gtk_file_dialog_set_default_filter (dialog, filter); + + gtk_file_dialog_open (dialog, + GTK_WINDOW (widget), + NULL, + sysprof_window_open_capture_cb, + g_object_ref (widget)); +} + static void sysprof_window_set_document (SysprofWindow *self, SysprofDocument *document) @@ -137,6 +186,8 @@ sysprof_window_class_init (SysprofWindowClass *klass) gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/sysprof/sysprof-window.ui"); + gtk_widget_class_install_action (widget_class, "win.open-capture", NULL, sysprof_window_open_capture_action); + g_type_ensure (SYSPROF_TYPE_DOCUMENT); g_type_ensure (SYSPROF_TYPE_SESSION); } diff --git a/src/sysprof/sysprof-window.ui b/src/sysprof/sysprof-window.ui index 71ac6688..563124ca 100644 --- a/src/sysprof/sysprof-window.ui +++ b/src/sysprof/sysprof-window.ui @@ -6,6 +6,16 @@ +
diff --git a/src/sysprof/sysprof.gresource.xml b/src/sysprof/sysprof.gresource.xml index 141202a9..1243ffcb 100644 --- a/src/sysprof/sysprof.gresource.xml +++ b/src/sysprof/sysprof.gresource.xml @@ -4,6 +4,7 @@ gtk/help-overlay.ui gtk/menus.ui sysprof-greeter.ui + sysprof-recording-pad.ui sysprof-window.ui From d9b42ec2193ed312df11a483527539763d8068e0 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 7 Jul 2023 17:00:31 -0700 Subject: [PATCH 0742/1030] sysprof: wire up close_request to recording pad --- src/sysprof/sysprof-recording-pad.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/sysprof/sysprof-recording-pad.c b/src/sysprof/sysprof-recording-pad.c index a77c6a4d..58633af1 100644 --- a/src/sysprof/sysprof-recording-pad.c +++ b/src/sysprof/sysprof-recording-pad.c @@ -110,11 +110,14 @@ sysprof_recording_pad_class_init (SysprofRecordingPadClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + GtkWindowClass *window_class = GTK_WINDOW_CLASS (klass); object_class->dispose = sysprof_recording_pad_dispose; object_class->get_property = sysprof_recording_pad_get_property; object_class->set_property = sysprof_recording_pad_set_property; + window_class->close_request = sysprof_recording_pad_close_request; + properties [PROP_RECORDING] = g_param_spec_object ("recording", NULL, NULL, SYSPROF_TYPE_RECORDING, From b48ff9d0141288b10a1987da9f3564bbeb964b07 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 7 Jul 2023 17:12:34 -0700 Subject: [PATCH 0743/1030] sysprof: add dialog to show files embedded in capture This will likely need search and the ability to open the files and/or display their content. --- src/sysprof/meson.build | 1 + src/sysprof/sysprof-files-dialog.c | 126 ++++++++++++++++++++++++++++ src/sysprof/sysprof-files-dialog.h | 35 ++++++++ src/sysprof/sysprof-files-dialog.ui | 65 ++++++++++++++ src/sysprof/sysprof-window.c | 18 ++++ src/sysprof/sysprof-window.ui | 1 + src/sysprof/sysprof.gresource.xml | 1 + 7 files changed, 247 insertions(+) create mode 100644 src/sysprof/sysprof-files-dialog.c create mode 100644 src/sysprof/sysprof-files-dialog.h create mode 100644 src/sysprof/sysprof-files-dialog.ui diff --git a/src/sysprof/meson.build b/src/sysprof/meson.build index dee2c4f1..fb21b295 100644 --- a/src/sysprof/meson.build +++ b/src/sysprof/meson.build @@ -1,6 +1,7 @@ sysprof_sources = [ 'main.c', 'sysprof-application.c', + 'sysprof-files-dialog.c', 'sysprof-greeter.c', 'sysprof-recording-pad.c', 'sysprof-window.c', diff --git a/src/sysprof/sysprof-files-dialog.c b/src/sysprof/sysprof-files-dialog.c new file mode 100644 index 00000000..37dab782 --- /dev/null +++ b/src/sysprof/sysprof-files-dialog.c @@ -0,0 +1,126 @@ +/* sysprof-files-dialog.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-files-dialog.h" + +struct _SysprofFilesDialog +{ + AdwWindow parent_instance; + + SysprofDocument *document; +}; + +enum { + PROP_0, + PROP_DOCUMENT, + N_PROPS +}; + +G_DEFINE_FINAL_TYPE (SysprofFilesDialog, sysprof_files_dialog, ADW_TYPE_WINDOW) + +static GParamSpec *properties [N_PROPS]; + +static void +sysprof_files_dialog_dispose (GObject *object) +{ + SysprofFilesDialog *self = (SysprofFilesDialog *)object; + + gtk_widget_dispose_template (GTK_WIDGET (self), SYSPROF_TYPE_FILES_DIALOG); + + g_clear_object (&self->document); + + G_OBJECT_CLASS (sysprof_files_dialog_parent_class)->dispose (object); +} + +static void +sysprof_files_dialog_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofFilesDialog *self = SYSPROF_FILES_DIALOG (object); + + switch (prop_id) + { + case PROP_DOCUMENT: + g_value_set_object (value, self->document); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_files_dialog_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SysprofFilesDialog *self = SYSPROF_FILES_DIALOG (object); + + switch (prop_id) + { + case PROP_DOCUMENT: + self->document = g_value_dup_object (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_files_dialog_class_init (SysprofFilesDialogClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->dispose = sysprof_files_dialog_dispose; + object_class->get_property = sysprof_files_dialog_get_property; + object_class->set_property = sysprof_files_dialog_set_property; + + properties[PROP_DOCUMENT] = + g_param_spec_object ("document", NULL, NULL, + SYSPROF_TYPE_DOCUMENT, + (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | 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-files-dialog.ui"); +} + +static void +sysprof_files_dialog_init (SysprofFilesDialog *self) +{ + gtk_widget_init_template (GTK_WIDGET (self)); +} + +GtkWidget * +sysprof_files_dialog_new (SysprofDocument *document) +{ + g_return_val_if_fail (SYSPROF_IS_DOCUMENT (document), NULL); + + return g_object_new (SYSPROF_TYPE_FILES_DIALOG, + "document", document, + NULL); +} diff --git a/src/sysprof/sysprof-files-dialog.h b/src/sysprof/sysprof-files-dialog.h new file mode 100644 index 00000000..6a9f535d --- /dev/null +++ b/src/sysprof/sysprof-files-dialog.h @@ -0,0 +1,35 @@ +/* sysprof-files-dialog.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +#include + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_FILES_DIALOG (sysprof_files_dialog_get_type()) + +G_DECLARE_FINAL_TYPE (SysprofFilesDialog, sysprof_files_dialog, SYSPROF, FILES_DIALOG, AdwWindow) + +GtkWidget *sysprof_files_dialog_new (SysprofDocument *document); + +G_END_DECLS diff --git a/src/sysprof/sysprof-files-dialog.ui b/src/sysprof/sysprof-files-dialog.ui new file mode 100644 index 00000000..96288ef7 --- /dev/null +++ b/src/sysprof/sysprof-files-dialog.ui @@ -0,0 +1,65 @@ + + + + diff --git a/src/sysprof/sysprof-window.c b/src/sysprof/sysprof-window.c index bb0c2b49..29226395 100644 --- a/src/sysprof/sysprof-window.c +++ b/src/sysprof/sysprof-window.c @@ -24,6 +24,7 @@ #include +#include "sysprof-files-dialog.h" #include "sysprof-window.h" struct _SysprofWindow @@ -93,6 +94,22 @@ sysprof_window_open_capture_action (GtkWidget *widget, g_object_ref (widget)); } +static void +sysprof_window_show_embedded_files_action (GtkWidget *widget, + const char *action_name, + GVariant *param) +{ + SysprofWindow *self = (SysprofWindow *)widget; + GtkWidget *dialog; + + g_assert (SYSPROF_IS_WINDOW (self)); + g_assert (SYSPROF_IS_DOCUMENT (self->document)); + + dialog = sysprof_files_dialog_new (self->document); + gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (self)); + gtk_window_present (GTK_WINDOW (dialog)); +} + static void sysprof_window_set_document (SysprofWindow *self, SysprofDocument *document) @@ -187,6 +204,7 @@ sysprof_window_class_init (SysprofWindowClass *klass) gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/sysprof/sysprof-window.ui"); gtk_widget_class_install_action (widget_class, "win.open-capture", NULL, sysprof_window_open_capture_action); + gtk_widget_class_install_action (widget_class, "capture.show-embedded-files", NULL, sysprof_window_show_embedded_files_action); g_type_ensure (SYSPROF_TYPE_DOCUMENT); g_type_ensure (SYSPROF_TYPE_SESSION); diff --git a/src/sysprof/sysprof-window.ui b/src/sysprof/sysprof-window.ui index 563124ca..d6c78c2a 100644 --- a/src/sysprof/sysprof-window.ui +++ b/src/sysprof/sysprof-window.ui @@ -138,6 +138,7 @@
Show Embedded Files… + capture.show-embedded-files Show Embedded Metadata… diff --git a/src/sysprof/sysprof.gresource.xml b/src/sysprof/sysprof.gresource.xml index 1243ffcb..53f801ac 100644 --- a/src/sysprof/sysprof.gresource.xml +++ b/src/sysprof/sysprof.gresource.xml @@ -3,6 +3,7 @@ gtk/help-overlay.ui gtk/menus.ui + sysprof-files-dialog.ui sysprof-greeter.ui sysprof-recording-pad.ui sysprof-window.ui From d5a13ccdb4b5045199875c85480cb6b246a8c5c2 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 7 Jul 2023 17:36:56 -0700 Subject: [PATCH 0744/1030] libsysprof-profile: add duration property for recording So we can use this to update a recording timer view. --- src/libsysprof-profile/sysprof-recording.c | 74 +++++++++++++++++++++- src/libsysprof-profile/sysprof-recording.h | 2 + 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/src/libsysprof-profile/sysprof-recording.c b/src/libsysprof-profile/sysprof-recording.c index 0a8bdf76..2ee48caf 100644 --- a/src/libsysprof-profile/sysprof-recording.c +++ b/src/libsysprof-profile/sysprof-recording.c @@ -36,6 +36,10 @@ struct _SysprofRecording { GObject parent_instance; + /* Used to calculate the duration of the recording */ + gint64 start_time; + gint64 end_time; + /* Diagnostics that may be added by instruments during the recording. * Some may be fatal, meaning that they stop the recording when the * diagnostic is submitted. That can happen in situations like @@ -71,11 +75,14 @@ struct _SysprofRecording enum { PROP_0, + PROP_DURATION, N_PROPS }; G_DEFINE_FINAL_TYPE (SysprofRecording, sysprof_recording, G_TYPE_OBJECT) +static GParamSpec *properties[N_PROPS]; + static DexFuture * _sysprof_recording_spawn (SysprofSpawnable *spawnable) { @@ -128,10 +135,13 @@ sysprof_recording_fiber (gpointer user_data) else monitor = dex_future_new_infinite (); + self->start_time = g_get_monotonic_time (); + /* Wait for messages on our channel or the recording to complete */ for (;;) { g_autoptr(DexFuture) message = dex_channel_receive (self->channel); + g_autoptr(DexFuture) duration = dex_timeout_new_seconds (1); /* Wait for either recording of all instruments to complete or a * message from our channel with what to do next. @@ -139,8 +149,10 @@ sysprof_recording_fiber (gpointer user_data) if (!dex_await (dex_future_first (dex_ref (record), dex_ref (message), dex_ref (monitor), + dex_ref (duration), NULL), - &error)) + &error) && + !g_error_matches (error, DEX_ERROR, DEX_ERROR_TIMED_OUT)) goto stop_recording; /* If record is not pending, then everything resolved/rejected */ @@ -160,11 +172,17 @@ sysprof_recording_fiber (gpointer user_data) goto stop_recording; } } + + /* Update duration each pass through the loop */ + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DURATION]); } stop_recording: end_time = SYSPROF_CAPTURE_CURRENT_TIME; + self->end_time = g_get_monotonic_time (); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DURATION]); + /* Signal cancellable so that anything lingering has a chance to be * cleaned up, cascading into other subsystems. */ @@ -211,12 +229,39 @@ sysprof_recording_finalize (GObject *object) G_OBJECT_CLASS (sysprof_recording_parent_class)->finalize (object); } +static void +sysprof_recording_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofRecording *self = SYSPROF_RECORDING (object); + + switch (prop_id) + { + case PROP_DURATION: + g_value_set_int64 (value, sysprof_recording_get_duration (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + static void sysprof_recording_class_init (SysprofRecordingClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = sysprof_recording_finalize; + object_class->get_property = sysprof_recording_get_property; + + properties [PROP_DURATION] = + g_param_spec_int64 ("duration", NULL, NULL, + 0, G_MAXINT64, 0, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); } static void @@ -587,3 +632,30 @@ sysprof_recording_list_diagnostics (SysprofRecording *self) return g_object_ref (G_LIST_MODEL (self->diagnostics)); } + +/** + * sysprof_recording_get_duration: + * @self: a #SysprofRecording + * + * Gets the recording duration in microseconds, which is the same + * precision used by g_get_monotonic_time(). Use %G_USEC_PER_SEC to + * get the time in seconds. + * + * Returns: the duration of the recording, or 0 + */ +gint64 +sysprof_recording_get_duration (SysprofRecording *self) +{ + gint64 start_time; + gint64 end_time; + + g_return_val_if_fail (SYSPROF_IS_RECORDING (self), 0); + + if (!(start_time = self->start_time)) + return 0; + + if (!(end_time = self->end_time)) + end_time = g_get_monotonic_time (); + + return end_time - start_time; +} diff --git a/src/libsysprof-profile/sysprof-recording.h b/src/libsysprof-profile/sysprof-recording.h index c9d9efa2..54cf8db6 100644 --- a/src/libsysprof-profile/sysprof-recording.h +++ b/src/libsysprof-profile/sysprof-recording.h @@ -34,6 +34,8 @@ G_DECLARE_FINAL_TYPE (SysprofRecording, sysprof_recording, SYSPROF, RECORDING, G SYSPROF_AVAILABLE_IN_ALL GListModel *sysprof_recording_list_diagnostics (SysprofRecording *self); SYSPROF_AVAILABLE_IN_ALL +gint64 sysprof_recording_get_duration (SysprofRecording *self); +SYSPROF_AVAILABLE_IN_ALL void sysprof_recording_wait_async (SysprofRecording *self, GCancellable *cancellable, GAsyncReadyCallback callback, From 7847aa1a745a6700db3942590aadb8169b363d17 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 7 Jul 2023 17:37:04 -0700 Subject: [PATCH 0745/1030] sysprof: show recording duration --- src/sysprof/sysprof-recording-pad.c | 12 ++++++++++++ src/sysprof/sysprof-recording-pad.ui | 16 ++++++++++------ 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/sysprof/sysprof-recording-pad.c b/src/sysprof/sysprof-recording-pad.c index 58633af1..7d78ff7e 100644 --- a/src/sysprof/sysprof-recording-pad.c +++ b/src/sysprof/sysprof-recording-pad.c @@ -42,6 +42,17 @@ G_DEFINE_FINAL_TYPE (SysprofRecordingPad, sysprof_recording_pad, ADW_TYPE_WINDOW static GParamSpec *properties[N_PROPS]; +static char * +format_time (GObject *unused, + gint64 duration) +{ + double elapsed = duration / (double)G_USEC_PER_SEC; + int minutes = floor (elapsed / 60); + int seconds = floor (elapsed - (minutes * 60)); + + return g_strdup_printf ("%02u:%02u", minutes, seconds); +} + static gboolean sysprof_recording_pad_close_request (GtkWindow *window) { @@ -127,6 +138,7 @@ sysprof_recording_pad_class_init (SysprofRecordingPadClass *klass) gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/sysprof/sysprof-recording-pad.ui"); gtk_widget_class_bind_template_child (widget_class, SysprofRecordingPad, stop_button); + gtk_widget_class_bind_template_callback (widget_class, format_time); } static void diff --git a/src/sysprof/sysprof-recording-pad.ui b/src/sysprof/sysprof-recording-pad.ui index 40a1810d..1f6871e0 100644 --- a/src/sysprof/sysprof-recording-pad.ui +++ b/src/sysprof/sysprof-recording-pad.ui @@ -1,6 +1,9 @@
+ + _Record Again… + win.record-capture + Open Recording… - win.open-capture + win.open-capture Save As…
-
- - Record Again… - -
Preferences @@ -193,11 +198,11 @@ Callgraph Hide System Libraries - win.callgraph.hide-system-libraries + win.callgraph.hide-system-libraries Include Threads - win.callgraph.include-threads + win.callgraph.include-threads
From e6a769b386b520f7ae6239e4ee97e5e524402ad0 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 10 Jul 2023 13:54:45 -0700 Subject: [PATCH 0809/1030] sysprof: add stack traces above callgraph This still needs to be put into a proper selectable container, but having the chart there gives me some idea of how things look for now. --- src/sysprof/sysprof-samples-section.c | 4 +++ src/sysprof/sysprof-samples-section.ui | 38 ++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/src/sysprof/sysprof-samples-section.c b/src/sysprof/sysprof-samples-section.c index d8efb01b..71fe3e6a 100644 --- a/src/sysprof/sysprof-samples-section.c +++ b/src/sysprof/sysprof-samples-section.c @@ -54,6 +54,10 @@ sysprof_samples_section_class_init (SysprofSamplesSectionClass *klass) gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/sysprof/sysprof-samples-section.ui"); gtk_widget_class_bind_template_child (widget_class, SysprofSamplesSection, callgraph_view); + g_type_ensure (SYSPROF_TYPE_CHART); + g_type_ensure (SYSPROF_TYPE_XY_SERIES); + g_type_ensure (SYSPROF_TYPE_COLUMN_LAYER); + g_type_ensure (SYSPROF_TYPE_VALUE_AXIS); g_type_ensure (SYSPROF_TYPE_WEIGHTED_CALLGRAPH_VIEW); } diff --git a/src/sysprof/sysprof-samples-section.ui b/src/sysprof/sysprof-samples-section.ui index f95837e4..8b459290 100644 --- a/src/sysprof/sysprof-samples-section.ui +++ b/src/sysprof/sysprof-samples-section.ui @@ -5,6 +5,44 @@ vertical + + + 32 + + + Stack Traces + + + SysprofSamplesSection + + + + + 0 + 128 + + + + + + + + SysprofSamplesSection + + + + + + + + + + + + + + + true From 6f2a3ac74eb5bc2729a9a9bdfd00c8beb21bdf63 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 10 Jul 2023 14:04:20 -0700 Subject: [PATCH 0810/1030] sysprof: categorize sidebar sections --- src/sysprof/sysprof-section.c | 38 +++++++++++++++++++++++++++++++++++ src/sysprof/sysprof-section.h | 15 ++++++++------ src/sysprof/sysprof-sidebar.c | 22 ++++++++++++++++++++ src/sysprof/sysprof-window.ui | 6 ++++++ 4 files changed, 75 insertions(+), 6 deletions(-) diff --git a/src/sysprof/sysprof-section.c b/src/sysprof/sysprof-section.c index cf0bcb12..52e93fc2 100644 --- a/src/sysprof/sysprof-section.c +++ b/src/sysprof/sysprof-section.c @@ -25,6 +25,7 @@ typedef struct { + char *category; char *title; SysprofSession *session; } SysprofSectionPrivate; @@ -34,6 +35,7 @@ G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (SysprofSection, sysprof_section, GTK_TYPE_W enum { PROP_0, PROP_SESSION, + PROP_CATEGORY, PROP_TITLE, N_PROPS }; @@ -52,6 +54,7 @@ sysprof_section_dispose (GObject *object) g_clear_object (&priv->session); g_clear_pointer (&priv->title, g_free); + g_clear_pointer (&priv->category, g_free); G_OBJECT_CLASS (sysprof_section_parent_class)->dispose (object); } @@ -70,6 +73,10 @@ sysprof_section_get_property (GObject *object, g_value_set_object (value, sysprof_section_get_session (self)); break; + case PROP_CATEGORY: + g_value_set_string (value, sysprof_section_get_category (self)); + break; + case PROP_TITLE: g_value_set_string (value, sysprof_section_get_title (self)); break; @@ -93,6 +100,10 @@ sysprof_section_set_property (GObject *object, sysprof_section_set_session (self, g_value_get_object (value)); break; + case PROP_CATEGORY: + sysprof_section_set_category (self, g_value_get_string (value)); + break; + case PROP_TITLE: sysprof_section_set_title (self, g_value_get_string (value)); break; @@ -122,6 +133,11 @@ sysprof_section_class_init (SysprofSectionClass *klass) NULL, (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); + properties[PROP_CATEGORY] = + g_param_spec_string ("category", NULL, NULL, + NULL, + (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_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT); @@ -176,3 +192,25 @@ sysprof_section_set_title (SysprofSection *self, if (g_set_str (&priv->title, title)) g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_TITLE]); } + +const char * +sysprof_section_get_category (SysprofSection *self) +{ + SysprofSectionPrivate *priv = sysprof_section_get_instance_private (self); + + g_return_val_if_fail (SYSPROF_IS_SECTION (self), NULL); + + return priv->category; +} + +void +sysprof_section_set_category (SysprofSection *self, + const char *category) +{ + SysprofSectionPrivate *priv = sysprof_section_get_instance_private (self); + + g_return_if_fail (SYSPROF_IS_SECTION (self)); + + if (g_set_str (&priv->category, category)) + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_CATEGORY]); +} diff --git a/src/sysprof/sysprof-section.h b/src/sysprof/sysprof-section.h index ce2b81a3..3d7a23fc 100644 --- a/src/sysprof/sysprof-section.h +++ b/src/sysprof/sysprof-section.h @@ -35,11 +35,14 @@ struct _SysprofSectionClass GtkWidgetClass parent_class; }; -SysprofSession *sysprof_section_get_session (SysprofSection *self); -void sysprof_section_set_session (SysprofSection *self, - SysprofSession *session); -const char *sysprof_section_get_title (SysprofSection *self); -void sysprof_section_set_title (SysprofSection *self, - const char *title); +SysprofSession *sysprof_section_get_session (SysprofSection *self); +void sysprof_section_set_session (SysprofSection *self, + SysprofSession *session); +const char *sysprof_section_get_category (SysprofSection *self); +void sysprof_section_set_category (SysprofSection *self, + const char *category); +const char *sysprof_section_get_title (SysprofSection *self); +void sysprof_section_set_title (SysprofSection *self, + const char *title); G_END_DECLS diff --git a/src/sysprof/sysprof-sidebar.c b/src/sysprof/sysprof-sidebar.c index 50ca2d6f..87d94435 100644 --- a/src/sysprof/sysprof-sidebar.c +++ b/src/sysprof/sysprof-sidebar.c @@ -112,6 +112,24 @@ list_box_row_activated_cb (SysprofSidebar *self, gtk_stack_set_visible_child (stack, GTK_WIDGET (section)); } +static void +sysprof_sidebar_header_func (GtkListBoxRow *row, + GtkListBoxRow *before_row, + gpointer user_data) +{ + SysprofSection *section; + SysprofSection *before_section; + + if (before_row == NULL) + return; + + if ((section = g_object_get_data (G_OBJECT (row), "SECTION")) && + (before_section = g_object_get_data (G_OBJECT (before_row), "SECTION")) && + g_strcmp0 (sysprof_section_get_category (section), + sysprof_section_get_category (before_section)) != 0) + gtk_list_box_row_set_header (row, gtk_separator_new (GTK_ORIENTATION_HORIZONTAL)); +} + static void sysprof_sidebar_dispose (GObject *object) { @@ -203,4 +221,8 @@ sysprof_sidebar_init (SysprofSidebar *self) G_CONNECT_SWAPPED); gtk_widget_init_template (GTK_WIDGET (self)); + + gtk_list_box_set_header_func (self->list_box, + sysprof_sidebar_header_func, + NULL, NULL); } diff --git a/src/sysprof/sysprof-window.ui b/src/sysprof/sysprof-window.ui index d656adb9..72416072 100644 --- a/src/sysprof/sysprof-window.ui +++ b/src/sysprof/sysprof-window.ui @@ -99,6 +99,7 @@ true + callgraph SysprofWindow @@ -106,6 +107,7 @@ + processes SysprofWindow @@ -113,6 +115,7 @@ + processes SysprofWindow @@ -120,6 +123,7 @@ + processes SysprofWindow @@ -127,6 +131,7 @@ + auxiliary SysprofWindow @@ -134,6 +139,7 @@ + auxiliary SysprofWindow From a4276f5b8f851d1bacb9c7a314dda6530e683efd Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 10 Jul 2023 14:15:03 -0700 Subject: [PATCH 0811/1030] libsysprof-analyze: allow two pointers for augmentation This affords us the ability to shove memory statistics in the inline augmentation area for memprof data. --- .../sysprof-callgraph-private.h | 4 ++-- src/libsysprof-analyze/sysprof-callgraph.c | 20 ++++++++++--------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-callgraph-private.h b/src/libsysprof-analyze/sysprof-callgraph-private.h index 92f70640..5adeee63 100644 --- a/src/libsysprof-analyze/sysprof-callgraph-private.h +++ b/src/libsysprof-analyze/sysprof-callgraph-private.h @@ -34,7 +34,7 @@ typedef struct _SysprofCallgraphSummary SysprofSymbol *symbol; EggBitset *traceables; GPtrArray *callers; - gpointer augment; + gpointer augment[2]; } SysprofCallgraphSummary; struct _SysprofCallgraphNode @@ -44,7 +44,7 @@ struct _SysprofCallgraphNode SysprofCallgraphNode *next; SysprofCallgraphNode *children; SysprofCallgraphSummary *summary; - gpointer augment; + gpointer augment[2]; }; struct _SysprofCallgraph diff --git a/src/libsysprof-analyze/sysprof-callgraph.c b/src/libsysprof-analyze/sysprof-callgraph.c index 4af6ca0a..419f64e9 100644 --- a/src/libsysprof-analyze/sysprof-callgraph.c +++ b/src/libsysprof-analyze/sysprof-callgraph.c @@ -31,7 +31,8 @@ #include "eggbitset.h" -#define MAX_STACK_DEPTH 1024 +#define MAX_STACK_DEPTH 1024 +#define INLINE_AUGMENT_SIZE (GLIB_SIZEOF_VOID_P*2) static GType sysprof_callgraph_get_item_type (GListModel *model) @@ -74,7 +75,8 @@ static SysprofSymbol *untraceable; static void sysprof_callgraph_summary_free_all (SysprofCallgraphSummary *summary) { - g_clear_pointer (&summary->augment, g_free); + g_clear_pointer (&summary->augment[0], g_free); + summary->augment[1] = NULL; g_clear_pointer (&summary->callers, g_ptr_array_unref); g_clear_pointer (&summary->traceables, egg_bitset_unref); g_free (summary); @@ -83,7 +85,8 @@ sysprof_callgraph_summary_free_all (SysprofCallgraphSummary *summary) static void sysprof_callgraph_summary_free_self (SysprofCallgraphSummary *summary) { - summary->augment = NULL; + summary->augment[0] = NULL; + summary->augment[1] = NULL; g_clear_pointer (&summary->callers, g_ptr_array_unref); g_clear_pointer (&summary->traceables, egg_bitset_unref); g_free (summary); @@ -98,7 +101,6 @@ sysprof_callgraph_get_summary (SysprofCallgraph *self, if G_UNLIKELY (!(summary = g_hash_table_lookup (self->symbol_to_summary, symbol))) { summary = g_new0 (SysprofCallgraphSummary, 1); - summary->augment = NULL; summary->traceables = egg_bitset_new_empty (); summary->callers = g_ptr_array_new (); summary->symbol = symbol; @@ -399,7 +401,7 @@ _sysprof_callgraph_new_async (SysprofDocument *document, g_return_if_fail (G_IS_LIST_MODEL (traceables)); g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); - if (augment_size > GLIB_SIZEOF_VOID_P) + if (augment_size > INLINE_AUGMENT_SIZE) summary_free = (GDestroyNotify)sysprof_callgraph_summary_free_all; else summary_free = (GDestroyNotify)sysprof_callgraph_summary_free_self; @@ -441,7 +443,7 @@ get_augmentation (SysprofCallgraph *self, if (self->augment_size == 0) return NULL; - if (self->augment_size <= GLIB_SIZEOF_VOID_P) + if (self->augment_size <= INLINE_AUGMENT_SIZE) return augment_location; if (*augment_location == NULL) @@ -457,7 +459,7 @@ sysprof_callgraph_get_augment (SysprofCallgraph *self, if (node == NULL) node = &self->root; - return get_augmentation (self, &node->augment); + return get_augmentation (self, &node->augment[0]); } gpointer @@ -467,7 +469,7 @@ sysprof_callgraph_get_summary_augment (SysprofCallgraph *self, if (node == NULL) node = &self->root; - return get_augmentation (self, &node->summary->augment); + return get_augmentation (self, &node->summary->augment[0]); } gpointer @@ -480,7 +482,7 @@ _sysprof_callgraph_get_symbol_augment (SysprofCallgraph *self, g_return_val_if_fail (SYSPROF_IS_SYMBOL (symbol), NULL); if ((summary = g_hash_table_lookup (self->symbol_to_summary, symbol))) - return get_augmentation (self, &summary->augment); + return get_augmentation (self, &summary->augment[0]); return NULL; } From 9147d45e4a74b5262e9b0ab76c90484795186ac1 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 10 Jul 2023 14:22:44 -0700 Subject: [PATCH 0812/1030] libsysprof-gtk: start on memory callgraph view --- .../libsysprof-gtk.gresource.xml | 1 + src/libsysprof-gtk/meson.build | 2 + src/libsysprof-gtk/sysprof-gtk.h | 1 + .../sysprof-memory-callgraph-view.c | 363 ++++++++++++++++++ .../sysprof-memory-callgraph-view.h | 42 ++ .../sysprof-memory-callgraph-view.ui | 203 ++++++++++ 6 files changed, 612 insertions(+) create mode 100644 src/libsysprof-gtk/sysprof-memory-callgraph-view.c create mode 100644 src/libsysprof-gtk/sysprof-memory-callgraph-view.h create mode 100644 src/libsysprof-gtk/sysprof-memory-callgraph-view.ui diff --git a/src/libsysprof-gtk/libsysprof-gtk.gresource.xml b/src/libsysprof-gtk/libsysprof-gtk.gresource.xml index f4a262be..f1d9f83c 100644 --- a/src/libsysprof-gtk/libsysprof-gtk.gresource.xml +++ b/src/libsysprof-gtk/libsysprof-gtk.gresource.xml @@ -5,6 +5,7 @@ sysprof-mark-chart.ui sysprof-mark-chart-row.ui sysprof-mark-table.ui + sysprof-memory-callgraph-view.ui sysprof-track-view.ui sysprof-tracks-view.ui sysprof-weighted-callgraph-view.ui diff --git a/src/libsysprof-gtk/meson.build b/src/libsysprof-gtk/meson.build index 7031073c..317d2c06 100644 --- a/src/libsysprof-gtk/meson.build +++ b/src/libsysprof-gtk/meson.build @@ -8,6 +8,7 @@ libsysprof_gtk_public_sources = [ 'sysprof-line-layer.c', 'sysprof-mark-chart.c', 'sysprof-mark-table.c', + 'sysprof-memory-callgraph-view.c', 'sysprof-normalized-series.c', 'sysprof-normalized-series-item.c', 'sysprof-series.c', @@ -42,6 +43,7 @@ libsysprof_gtk_public_headers = [ 'sysprof-line-layer.h', 'sysprof-mark-chart.h', 'sysprof-mark-table.h', + 'sysprof-memory-callgraph-view.h', 'sysprof-normalized-series.h', 'sysprof-normalized-series-item.h', 'sysprof-series.h', diff --git a/src/libsysprof-gtk/sysprof-gtk.h b/src/libsysprof-gtk/sysprof-gtk.h index 26859a26..cdb6b50a 100644 --- a/src/libsysprof-gtk/sysprof-gtk.h +++ b/src/libsysprof-gtk/sysprof-gtk.h @@ -34,6 +34,7 @@ G_BEGIN_DECLS # include "sysprof-line-layer.h" # include "sysprof-mark-chart.h" # include "sysprof-mark-table.h" +# include "sysprof-memory-callgraph-view.h" # include "sysprof-normalized-series.h" # include "sysprof-normalized-series-item.h" # include "sysprof-series.h" diff --git a/src/libsysprof-gtk/sysprof-memory-callgraph-view.c b/src/libsysprof-gtk/sysprof-memory-callgraph-view.c new file mode 100644 index 00000000..f3b84cf2 --- /dev/null +++ b/src/libsysprof-gtk/sysprof-memory-callgraph-view.c @@ -0,0 +1,363 @@ +/* sysprof-memory-callgraph-view.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-callgraph-view-private.h" +#include "sysprof-memory-callgraph-view.h" +#include "sysprof-progress-cell-private.h" + +struct _SysprofMemoryCallgraphView +{ + SysprofCallgraphView parent_instance; + + GtkColumnViewColumn *callers_self_column; + GtkColumnViewColumn *callers_total_column; + GtkCustomSorter *callers_self_sorter; + GtkCustomSorter *callers_total_sorter; + + GtkColumnViewColumn *descendants_self_column; + GtkColumnViewColumn *descendants_total_column; + GtkCustomSorter *descendants_self_sorter; + GtkCustomSorter *descendants_total_sorter; + + GtkColumnViewColumn *functions_self_column; + GtkColumnViewColumn *functions_total_column; + GtkCustomSorter *functions_self_sorter; + GtkCustomSorter *functions_total_sorter; +}; + +struct _SysprofMemoryCallgraphViewClass +{ + SysprofCallgraphViewClass parent_class; +}; + +typedef struct _AugmentMemory +{ + gsize size; + gsize total; +} AugmentMemory; + +G_DEFINE_FINAL_TYPE (SysprofMemoryCallgraphView, sysprof_memory_callgraph_view, SYSPROF_TYPE_CALLGRAPH_VIEW) + +static void +augment_memory (SysprofCallgraph *callgraph, + SysprofCallgraphNode *node, + SysprofDocumentFrame *frame, + gboolean summarize, + gpointer user_data) +{ + AugmentMemory *cur; + AugmentMemory *sum; + gsize size; + + g_assert (SYSPROF_IS_CALLGRAPH (callgraph)); + g_assert (node != NULL); + g_assert (SYSPROF_IS_DOCUMENT_ALLOCATION (frame)); + g_assert (user_data == NULL); + + size = sysprof_document_allocation_get_size (SYSPROF_DOCUMENT_ALLOCATION (frame)); + + cur = sysprof_callgraph_get_augment (callgraph, node); + cur->size += size; + cur->total += size; + + if (summarize) + { + sum = sysprof_callgraph_get_summary_augment (callgraph, node); + sum->size += size; + sum->total += size; + } + + for (node = sysprof_callgraph_node_parent (node); + node != NULL; + node = sysprof_callgraph_node_parent (node)) + { + cur = sysprof_callgraph_get_augment (callgraph, node); + cur->total += size; + + if (summarize) + { + sum = sysprof_callgraph_get_summary_augment (callgraph, node); + sum->total += size; + } + } +} + +static double +get_total_fraction (GObject *item) +{ + g_autoptr(GObject) row = NULL; + + g_object_get (item, "item", &row, NULL); + + if (GTK_IS_TREE_LIST_ROW (row)) + { + GtkTreeListRow *tree_row = GTK_TREE_LIST_ROW (row); + SysprofCallgraphFrame *frame = SYSPROF_CALLGRAPH_FRAME (gtk_tree_list_row_get_item (tree_row)); + SysprofCallgraph *callgraph = sysprof_callgraph_frame_get_callgraph (frame); + AugmentMemory *sum = sysprof_callgraph_frame_get_augment (frame); + AugmentMemory *root = sysprof_callgraph_get_augment (callgraph, NULL); + + if (root->total == 0) + return 0; + + return sum->total / (double)root->total; + } + + return 0; +} + +static double +get_self_fraction (GObject *item) +{ + g_autoptr(GObject) row = NULL; + + g_object_get (item, "item", &row, NULL); + + if (GTK_IS_TREE_LIST_ROW (row)) + { + GtkTreeListRow *tree_row = GTK_TREE_LIST_ROW (row); + SysprofCallgraphFrame *frame = SYSPROF_CALLGRAPH_FRAME (gtk_tree_list_row_get_item (tree_row)); + SysprofCallgraph *callgraph = sysprof_callgraph_frame_get_callgraph (frame); + AugmentMemory *sum = sysprof_callgraph_frame_get_augment (frame); + AugmentMemory *root = sysprof_callgraph_get_augment (callgraph, NULL); + + if (root->total == 0) + return 0; + + return sum->size / (double)root->total; + } + + return .0; +} + +static double +functions_get_total_fraction (GObject *item) +{ + g_autoptr(SysprofCallgraphSymbol) sym = NULL; + + g_object_get (item, "item", &sym, NULL); + + if (SYSPROF_IS_CALLGRAPH_SYMBOL (sym)) + { + SysprofCallgraph *callgraph = sysprof_callgraph_symbol_get_callgraph (sym); + AugmentMemory *sum = sysprof_callgraph_symbol_get_summary_augment (sym); + AugmentMemory *root = sysprof_callgraph_get_augment (callgraph, NULL); + + if (root->total == 0) + return 0; + + return sum->total / (double)root->total; + } + + return 0; +} + +static double +functions_get_self_fraction (GObject *item) +{ + g_autoptr(SysprofCallgraphSymbol) sym = NULL; + + g_object_get (item, "item", &sym, NULL); + + if (SYSPROF_IS_CALLGRAPH_SYMBOL (sym)) + { + SysprofCallgraph *callgraph = sysprof_callgraph_symbol_get_callgraph (sym); + AugmentMemory *sum = sysprof_callgraph_symbol_get_summary_augment (sym); + AugmentMemory *root = sysprof_callgraph_get_augment (callgraph, NULL); + + if (root->total == 0) + return 0; + + return sum->size / (double)root->total; + } + + return 0; +} + +static int +descendants_sort_by_self (gconstpointer a, + gconstpointer b, + gpointer user_data) +{ + SysprofCallgraphFrame *frame_a = (SysprofCallgraphFrame *)a; + SysprofCallgraphFrame *frame_b = (SysprofCallgraphFrame *)b; + AugmentMemory *aug_a = sysprof_callgraph_frame_get_augment (frame_a); + AugmentMemory *aug_b = sysprof_callgraph_frame_get_augment (frame_b); + AugmentMemory *root = user_data; + double self_a = aug_a->size / (double)root->total; + double self_b = aug_b->size / (double)root->total; + + if (self_a < self_b) + return -1; + else if (self_a > self_b) + return 1; + else + return 0; +} + +static int +descendants_sort_by_total (gconstpointer a, + gconstpointer b, + gpointer user_data) +{ + SysprofCallgraphFrame *frame_a = (SysprofCallgraphFrame *)a; + SysprofCallgraphFrame *frame_b = (SysprofCallgraphFrame *)b; + AugmentMemory *aug_a = sysprof_callgraph_frame_get_augment (frame_a); + AugmentMemory *aug_b = sysprof_callgraph_frame_get_augment (frame_b); + AugmentMemory *root = user_data; + double total_a = aug_a->total / (double)root->total; + double total_b = aug_b->total / (double)root->total; + + if (total_a < total_b) + return -1; + else if (total_a > total_b) + return 1; + else + return 0; +} + +static int +functions_sort_by_self (gconstpointer a, + gconstpointer b, + gpointer user_data) +{ + SysprofCallgraphSymbol *sym_a = (SysprofCallgraphSymbol *)a; + SysprofCallgraphSymbol *sym_b = (SysprofCallgraphSymbol *)b; + AugmentMemory *aug_a = sysprof_callgraph_symbol_get_summary_augment (sym_a); + AugmentMemory *aug_b = sysprof_callgraph_symbol_get_summary_augment (sym_b); + AugmentMemory *root = user_data; + double self_a = aug_a->size / (double)root->total; + double self_b = aug_b->size / (double)root->total; + + if (self_a < self_b) + return -1; + else if (self_a > self_b) + return 1; + else + return 0; +} + +static int +functions_sort_by_total (gconstpointer a, + gconstpointer b, + gpointer user_data) +{ + SysprofCallgraphSymbol *sym_a = (SysprofCallgraphSymbol *)a; + SysprofCallgraphSymbol *sym_b = (SysprofCallgraphSymbol *)b; + AugmentMemory *aug_a = sysprof_callgraph_symbol_get_summary_augment (sym_a); + AugmentMemory *aug_b = sysprof_callgraph_symbol_get_summary_augment (sym_b); + AugmentMemory *root = user_data; + double total_a = aug_a->total / (double)root->total; + double total_b = aug_b->total / (double)root->total; + + if (total_a < total_b) + return -1; + else if (total_a > total_b) + return 1; + else + return 0; +} + +static void +sysprof_memory_callgraph_view_load (SysprofCallgraphView *view, + SysprofCallgraph *callgraph) +{ + SysprofMemoryCallgraphView *self = (SysprofMemoryCallgraphView *)view; + AugmentMemory *root; + + g_assert (SYSPROF_IS_MEMORY_CALLGRAPH_VIEW (self)); + g_assert (SYSPROF_IS_CALLGRAPH (callgraph)); + + root = sysprof_callgraph_get_augment (callgraph, NULL); + + gtk_custom_sorter_set_sort_func (self->descendants_self_sorter, + descendants_sort_by_self, root, NULL); + gtk_custom_sorter_set_sort_func (self->descendants_total_sorter, + descendants_sort_by_total, root, NULL); + + gtk_custom_sorter_set_sort_func (self->functions_self_sorter, + functions_sort_by_self, root, NULL); + gtk_custom_sorter_set_sort_func (self->functions_total_sorter, + functions_sort_by_total, root, NULL); + + gtk_custom_sorter_set_sort_func (self->callers_self_sorter, + functions_sort_by_self, root, NULL); + gtk_custom_sorter_set_sort_func (self->callers_total_sorter, + functions_sort_by_total, root, NULL); + + gtk_column_view_sort_by_column (SYSPROF_CALLGRAPH_VIEW (self)->callers_column_view, + self->callers_total_column, + GTK_SORT_DESCENDING); + gtk_column_view_sort_by_column (SYSPROF_CALLGRAPH_VIEW (self)->descendants_column_view, + self->descendants_total_column, + GTK_SORT_DESCENDING); + gtk_column_view_sort_by_column (SYSPROF_CALLGRAPH_VIEW (self)->functions_column_view, + self->functions_total_column, + GTK_SORT_DESCENDING); +} + +static void +sysprof_memory_callgraph_view_class_init (SysprofMemoryCallgraphViewClass *klass) +{ + SysprofCallgraphViewClass *callgraph_view_class = SYSPROF_CALLGRAPH_VIEW_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + callgraph_view_class->augment_size = sizeof (AugmentMemory); + callgraph_view_class->augment_func = augment_memory; + callgraph_view_class->load = sysprof_memory_callgraph_view_load; + + gtk_widget_class_set_template_from_resource (widget_class, "/libsysprof-gtk/sysprof-memory-callgraph-view.ui"); + + gtk_widget_class_bind_template_child (widget_class, SysprofMemoryCallgraphView, callers_self_column); + gtk_widget_class_bind_template_child (widget_class, SysprofMemoryCallgraphView, callers_total_column); + gtk_widget_class_bind_template_child (widget_class, SysprofMemoryCallgraphView, callers_self_sorter); + gtk_widget_class_bind_template_child (widget_class, SysprofMemoryCallgraphView, callers_total_sorter); + + gtk_widget_class_bind_template_child (widget_class, SysprofMemoryCallgraphView, descendants_self_column); + gtk_widget_class_bind_template_child (widget_class, SysprofMemoryCallgraphView, descendants_total_column); + gtk_widget_class_bind_template_child (widget_class, SysprofMemoryCallgraphView, descendants_self_sorter); + gtk_widget_class_bind_template_child (widget_class, SysprofMemoryCallgraphView, descendants_total_sorter); + + gtk_widget_class_bind_template_child (widget_class, SysprofMemoryCallgraphView, functions_self_column); + gtk_widget_class_bind_template_child (widget_class, SysprofMemoryCallgraphView, functions_total_column); + gtk_widget_class_bind_template_child (widget_class, SysprofMemoryCallgraphView, functions_self_sorter); + gtk_widget_class_bind_template_child (widget_class, SysprofMemoryCallgraphView, functions_total_sorter); + + gtk_widget_class_bind_template_callback (widget_class, get_self_fraction); + gtk_widget_class_bind_template_callback (widget_class, get_total_fraction); + gtk_widget_class_bind_template_callback (widget_class, functions_get_self_fraction); + gtk_widget_class_bind_template_callback (widget_class, functions_get_total_fraction); + + g_type_ensure (SYSPROF_TYPE_PROGRESS_CELL); +} + +static void +sysprof_memory_callgraph_view_init (SysprofMemoryCallgraphView *self) +{ + gtk_widget_init_template (GTK_WIDGET (self)); +} + +GtkWidget * +sysprof_memory_callgraph_view_new (void) +{ + return g_object_new (SYSPROF_TYPE_MEMORY_CALLGRAPH_VIEW, NULL); +} diff --git a/src/libsysprof-gtk/sysprof-memory-callgraph-view.h b/src/libsysprof-gtk/sysprof-memory-callgraph-view.h new file mode 100644 index 00000000..6db99547 --- /dev/null +++ b/src/libsysprof-gtk/sysprof-memory-callgraph-view.h @@ -0,0 +1,42 @@ +/* sysprof-memory-callgraph-view.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-callgraph-view.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_MEMORY_CALLGRAPH_VIEW (sysprof_memory_callgraph_view_get_type()) +#define SYSPROF_IS_MEMORY_CALLGRAPH_VIEW(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_MEMORY_CALLGRAPH_VIEW) +#define SYSPROF_MEMORY_CALLGRAPH_VIEW(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_MEMORY_CALLGRAPH_VIEW, SysprofMemoryCallgraphView) +#define SYSPROF_MEMORY_CALLGRAPH_VIEW_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_MEMORY_CALLGRAPH_VIEW, SysprofMemoryCallgraphViewClass) + +typedef struct _SysprofMemoryCallgraphView SysprofMemoryCallgraphView; +typedef struct _SysprofMemoryCallgraphViewClass SysprofMemoryCallgraphViewClass; + +SYSPROF_AVAILABLE_IN_ALL +GType sysprof_memory_callgraph_view_get_type (void) G_GNUC_CONST; +SYSPROF_AVAILABLE_IN_ALL +GtkWidget *sysprof_memory_callgraph_view_new (void); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofMemoryCallgraphView, g_object_unref) + +G_END_DECLS diff --git a/src/libsysprof-gtk/sysprof-memory-callgraph-view.ui b/src/libsysprof-gtk/sysprof-memory-callgraph-view.ui new file mode 100644 index 00000000..0efb97ac --- /dev/null +++ b/src/libsysprof-gtk/sysprof-memory-callgraph-view.ui @@ -0,0 +1,203 @@ + + + + From 0c51dff12474b4981d970b7ad7a30c11b91cafb8 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 10 Jul 2023 14:23:15 -0700 Subject: [PATCH 0813/1030] sysprof: add memory allocations section Still a bunch to do here to restore what we had previously, but this gets the section into place. --- src/sysprof/meson.build | 1 + src/sysprof/sysprof-memory-section.c | 68 +++++++++++++++++++++++++++ src/sysprof/sysprof-memory-section.h | 31 ++++++++++++ src/sysprof/sysprof-memory-section.ui | 40 ++++++++++++++++ src/sysprof/sysprof-window.c | 2 + src/sysprof/sysprof-window.ui | 8 ++++ src/sysprof/sysprof.gresource.xml | 1 + 7 files changed, 151 insertions(+) create mode 100644 src/sysprof/sysprof-memory-section.c create mode 100644 src/sysprof/sysprof-memory-section.h create mode 100644 src/sysprof/sysprof-memory-section.ui diff --git a/src/sysprof/meson.build b/src/sysprof/meson.build index dcb653c0..854b46ff 100644 --- a/src/sysprof/meson.build +++ b/src/sysprof/meson.build @@ -5,6 +5,7 @@ sysprof_sources = [ 'sysprof-greeter.c', 'sysprof-logs-section.c', 'sysprof-marks-section.c', + 'sysprof-memory-section.c', 'sysprof-metadata-section.c', 'sysprof-processes-section.c', 'sysprof-recording-pad.c', diff --git a/src/sysprof/sysprof-memory-section.c b/src/sysprof/sysprof-memory-section.c new file mode 100644 index 00000000..1281e96e --- /dev/null +++ b/src/sysprof/sysprof-memory-section.c @@ -0,0 +1,68 @@ +/* sysprof-memory-section.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include + +#include "sysprof-memory-section.h" + +struct _SysprofMemorySection +{ + SysprofSection parent_instance; + + SysprofWeightedCallgraphView *callgraph_view; +}; + +G_DEFINE_FINAL_TYPE (SysprofMemorySection, sysprof_memory_section, SYSPROF_TYPE_SECTION) + +static void +sysprof_memory_section_dispose (GObject *object) +{ + SysprofMemorySection *self = (SysprofMemorySection *)object; + + gtk_widget_dispose_template (GTK_WIDGET (self), SYSPROF_TYPE_MEMORY_SECTION); + + G_OBJECT_CLASS (sysprof_memory_section_parent_class)->dispose (object); +} + +static void +sysprof_memory_section_class_init (SysprofMemorySectionClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->dispose = sysprof_memory_section_dispose; + + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/sysprof/sysprof-memory-section.ui"); + gtk_widget_class_bind_template_child (widget_class, SysprofMemorySection, callgraph_view); + + g_type_ensure (SYSPROF_TYPE_CHART); + g_type_ensure (SYSPROF_TYPE_XY_SERIES); + g_type_ensure (SYSPROF_TYPE_COLUMN_LAYER); + g_type_ensure (SYSPROF_TYPE_VALUE_AXIS); + g_type_ensure (SYSPROF_TYPE_MEMORY_CALLGRAPH_VIEW); +} + +static void +sysprof_memory_section_init (SysprofMemorySection *self) +{ + gtk_widget_init_template (GTK_WIDGET (self)); +} diff --git a/src/sysprof/sysprof-memory-section.h b/src/sysprof/sysprof-memory-section.h new file mode 100644 index 00000000..a437d2e7 --- /dev/null +++ b/src/sysprof/sysprof-memory-section.h @@ -0,0 +1,31 @@ +/* sysprof-memory-section.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-section.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_MEMORY_SECTION (sysprof_memory_section_get_type()) + +G_DECLARE_FINAL_TYPE (SysprofMemorySection, sysprof_memory_section, SYSPROF, MEMORY_SECTION, SysprofSection) + +G_END_DECLS diff --git a/src/sysprof/sysprof-memory-section.ui b/src/sysprof/sysprof-memory-section.ui new file mode 100644 index 00000000..7cccaaf7 --- /dev/null +++ b/src/sysprof/sysprof-memory-section.ui @@ -0,0 +1,40 @@ + + + + + diff --git a/src/sysprof/sysprof-window.c b/src/sysprof/sysprof-window.c index b3d95f59..5f54e8e1 100644 --- a/src/sysprof/sysprof-window.c +++ b/src/sysprof/sysprof-window.c @@ -28,6 +28,7 @@ #include "sysprof-greeter.h" #include "sysprof-logs-section.h" #include "sysprof-marks-section.h" +#include "sysprof-memory-section.h" #include "sysprof-metadata-section.h" #include "sysprof-processes-section.h" #include "sysprof-samples-section.h" @@ -199,6 +200,7 @@ sysprof_window_class_init (SysprofWindowClass *klass) g_type_ensure (SYSPROF_TYPE_FILES_SECTION); g_type_ensure (SYSPROF_TYPE_LOGS_SECTION); g_type_ensure (SYSPROF_TYPE_MARKS_SECTION); + g_type_ensure (SYSPROF_TYPE_MEMORY_SECTION); g_type_ensure (SYSPROF_TYPE_METADATA_SECTION); g_type_ensure (SYSPROF_TYPE_PROCESSES_SECTION); g_type_ensure (SYSPROF_TYPE_SAMPLES_SECTION); diff --git a/src/sysprof/sysprof-window.ui b/src/sysprof/sysprof-window.ui index 72416072..c2d8d9b0 100644 --- a/src/sysprof/sysprof-window.ui +++ b/src/sysprof/sysprof-window.ui @@ -105,6 +105,14 @@ + + + callgraph + + SysprofWindow + + + processes diff --git a/src/sysprof/sysprof.gresource.xml b/src/sysprof/sysprof.gresource.xml index 6bea8c39..655506b9 100644 --- a/src/sysprof/sysprof.gresource.xml +++ b/src/sysprof/sysprof.gresource.xml @@ -7,6 +7,7 @@ sysprof-greeter.ui sysprof-logs-section.ui sysprof-marks-section.ui + sysprof-memory-section.ui sysprof-metadata-section.ui sysprof-processes-section.ui sysprof-recording-pad.ui From 77a305f3bf7dfb61592f2a767de3073dedaa703b Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 10 Jul 2023 14:35:38 -0700 Subject: [PATCH 0814/1030] libsysprof-analyze: runtime protection against NULL names Shouldn't happen, but at least don't crash. --- src/libsysprof-analyze/sysprof-symbol-private.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-symbol-private.h b/src/libsysprof-analyze/sysprof-symbol-private.h index 545d9f48..0fb79111 100644 --- a/src/libsysprof-analyze/sysprof-symbol-private.h +++ b/src/libsysprof-analyze/sysprof-symbol-private.h @@ -76,6 +76,9 @@ _sysprof_symbol_equal (const SysprofSymbol *a, if (a->kind != b->kind) return FALSE; + if (a->name == NULL || b->name == NULL) + return FALSE; + return strcmp (a->name, b->name) == 0; } From dc66e93eec2f26ec5c60faf61e34590c56075a5e Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 10 Jul 2023 14:39:33 -0700 Subject: [PATCH 0815/1030] sysprof: ensure initial row is selected --- src/sysprof/sysprof-sidebar.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sysprof/sysprof-sidebar.c b/src/sysprof/sysprof-sidebar.c index 87d94435..cc40b744 100644 --- a/src/sysprof/sysprof-sidebar.c +++ b/src/sysprof/sysprof-sidebar.c @@ -82,6 +82,8 @@ sysprof_sidebar_bind_cb (SysprofSidebar *self, sysprof_sidebar_create_row, self, NULL); + + gtk_list_box_select_row (self->list_box, gtk_list_box_get_row_at_index (self->list_box, 0)); } static void From 7dbb5abb6207a709b2669bda7be8429d06103b49 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 10 Jul 2023 14:44:26 -0700 Subject: [PATCH 0816/1030] sysprof: add stack traces above memory callgraph --- src/sysprof/sysprof-memory-section.ui | 38 +++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/sysprof/sysprof-memory-section.ui b/src/sysprof/sysprof-memory-section.ui index 7cccaaf7..5e166a8a 100644 --- a/src/sysprof/sysprof-memory-section.ui +++ b/src/sysprof/sysprof-memory-section.ui @@ -5,6 +5,44 @@ vertical + + + 32 + + + Stack Traces + + + SysprofMemorySection + + + + + 0 + 128 + + + + + + + + SysprofMemorySection + + + + + + + + + + + + + + + true From 79f6984e3e9204e54fc4866867ab5d3977060f56 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 10 Jul 2023 14:49:33 -0700 Subject: [PATCH 0817/1030] sysprof: add some mark icons to marks section --- src/sysprof/icons/scalable/actions/mark-chart-symbolic.svg | 2 ++ src/sysprof/icons/scalable/actions/mark-table-symbolic.svg | 2 ++ src/sysprof/sysprof-marks-section.ui | 4 ++-- src/sysprof/sysprof.gresource.xml | 2 ++ 4 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 src/sysprof/icons/scalable/actions/mark-chart-symbolic.svg create mode 100644 src/sysprof/icons/scalable/actions/mark-table-symbolic.svg diff --git a/src/sysprof/icons/scalable/actions/mark-chart-symbolic.svg b/src/sysprof/icons/scalable/actions/mark-chart-symbolic.svg new file mode 100644 index 00000000..6d2bb7db --- /dev/null +++ b/src/sysprof/icons/scalable/actions/mark-chart-symbolic.svg @@ -0,0 +1,2 @@ + + diff --git a/src/sysprof/icons/scalable/actions/mark-table-symbolic.svg b/src/sysprof/icons/scalable/actions/mark-table-symbolic.svg new file mode 100644 index 00000000..aaaf54af --- /dev/null +++ b/src/sysprof/icons/scalable/actions/mark-table-symbolic.svg @@ -0,0 +1,2 @@ + + diff --git a/src/sysprof/sysprof-marks-section.ui b/src/sysprof/sysprof-marks-section.ui index 59835a4c..692fe718 100644 --- a/src/sysprof/sysprof-marks-section.ui +++ b/src/sysprof/sysprof-marks-section.ui @@ -11,7 +11,7 @@ Mark Chart - table-symbolic + mark-chart-symbolic @@ -24,7 +24,7 @@ Mark Table - table-symbolic + mark-table-symbolic diff --git a/src/sysprof/sysprof.gresource.xml b/src/sysprof/sysprof.gresource.xml index 655506b9..15d06dfb 100644 --- a/src/sysprof/sysprof.gresource.xml +++ b/src/sysprof/sysprof.gresource.xml @@ -14,5 +14,7 @@ sysprof-samples-section.ui sysprof-sidebar.ui sysprof-window.ui + icons/scalable/actions/mark-chart-symbolic.svg + icons/scalable/actions/mark-table-symbolic.svg From fb85c2078ec1aa4668b770167cbce2f455d4bba4 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 10 Jul 2023 14:59:09 -0700 Subject: [PATCH 0818/1030] sysprof: start on icons for sections --- .../scalable/actions/system-log-symbolic.svg | 23 +++++++++++ src/sysprof/sysprof-section.c | 38 +++++++++++++++++++ src/sysprof/sysprof-section.h | 21 +++++----- src/sysprof/sysprof-sidebar.c | 23 +++++++++-- src/sysprof/sysprof-window.ui | 5 +++ src/sysprof/sysprof.gresource.xml | 1 + 6 files changed, 98 insertions(+), 13 deletions(-) create mode 100644 src/sysprof/icons/scalable/actions/system-log-symbolic.svg diff --git a/src/sysprof/icons/scalable/actions/system-log-symbolic.svg b/src/sysprof/icons/scalable/actions/system-log-symbolic.svg new file mode 100644 index 00000000..df132b2a --- /dev/null +++ b/src/sysprof/icons/scalable/actions/system-log-symbolic.svg @@ -0,0 +1,23 @@ + + + + + + + + image/svg+xml + + Gnome Symbolic Icon Theme + + + + + + + Gnome Symbolic Icon Theme + + + + + + diff --git a/src/sysprof/sysprof-section.c b/src/sysprof/sysprof-section.c index 52e93fc2..581edded 100644 --- a/src/sysprof/sysprof-section.c +++ b/src/sysprof/sysprof-section.c @@ -26,6 +26,7 @@ typedef struct { char *category; + char *icon_name; char *title; SysprofSession *session; } SysprofSectionPrivate; @@ -36,6 +37,7 @@ enum { PROP_0, PROP_SESSION, PROP_CATEGORY, + PROP_ICON_NAME, PROP_TITLE, N_PROPS }; @@ -55,6 +57,7 @@ sysprof_section_dispose (GObject *object) g_clear_object (&priv->session); g_clear_pointer (&priv->title, g_free); g_clear_pointer (&priv->category, g_free); + g_clear_pointer (&priv->icon_name, g_free); G_OBJECT_CLASS (sysprof_section_parent_class)->dispose (object); } @@ -77,6 +80,10 @@ sysprof_section_get_property (GObject *object, g_value_set_string (value, sysprof_section_get_category (self)); break; + case PROP_ICON_NAME: + g_value_set_string (value, sysprof_section_get_icon_name (self)); + break; + case PROP_TITLE: g_value_set_string (value, sysprof_section_get_title (self)); break; @@ -104,6 +111,10 @@ sysprof_section_set_property (GObject *object, sysprof_section_set_category (self, g_value_get_string (value)); break; + case PROP_ICON_NAME: + sysprof_section_set_icon_name (self, g_value_get_string (value)); + break; + case PROP_TITLE: sysprof_section_set_title (self, g_value_get_string (value)); break; @@ -138,6 +149,11 @@ sysprof_section_class_init (SysprofSectionClass *klass) NULL, (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); + properties[PROP_ICON_NAME] = + g_param_spec_string ("icon-name", NULL, NULL, + NULL, + (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_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT); @@ -214,3 +230,25 @@ sysprof_section_set_category (SysprofSection *self, if (g_set_str (&priv->category, category)) g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_CATEGORY]); } + +const char * +sysprof_section_get_icon_name (SysprofSection *self) +{ + SysprofSectionPrivate *priv = sysprof_section_get_instance_private (self); + + g_return_val_if_fail (SYSPROF_IS_SECTION (self), NULL); + + return priv->icon_name; +} + +void +sysprof_section_set_icon_name (SysprofSection *self, + const char *icon_name) +{ + SysprofSectionPrivate *priv = sysprof_section_get_instance_private (self); + + g_return_if_fail (SYSPROF_IS_SECTION (self)); + + if (g_set_str (&priv->icon_name, icon_name)) + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ICON_NAME]); +} diff --git a/src/sysprof/sysprof-section.h b/src/sysprof/sysprof-section.h index 3d7a23fc..edc9e464 100644 --- a/src/sysprof/sysprof-section.h +++ b/src/sysprof/sysprof-section.h @@ -35,14 +35,17 @@ struct _SysprofSectionClass GtkWidgetClass parent_class; }; -SysprofSession *sysprof_section_get_session (SysprofSection *self); -void sysprof_section_set_session (SysprofSection *self, - SysprofSession *session); -const char *sysprof_section_get_category (SysprofSection *self); -void sysprof_section_set_category (SysprofSection *self, - const char *category); -const char *sysprof_section_get_title (SysprofSection *self); -void sysprof_section_set_title (SysprofSection *self, - const char *title); +SysprofSession *sysprof_section_get_session (SysprofSection *self); +void sysprof_section_set_session (SysprofSection *self, + SysprofSession *session); +const char *sysprof_section_get_category (SysprofSection *self); +void sysprof_section_set_category (SysprofSection *self, + const char *category); +const char *sysprof_section_get_icon_name (SysprofSection *self); +void sysprof_section_set_icon_name (SysprofSection *self, + const char *icon_name); +const char *sysprof_section_get_title (SysprofSection *self); +void sysprof_section_set_title (SysprofSection *self, + const char *title); G_END_DECLS diff --git a/src/sysprof/sysprof-sidebar.c b/src/sysprof/sysprof-sidebar.c index cc40b744..3b79fe53 100644 --- a/src/sysprof/sysprof-sidebar.c +++ b/src/sysprof/sysprof-sidebar.c @@ -48,16 +48,31 @@ sysprof_sidebar_create_row (gpointer item, SysprofSidebar *sidebar = user_data; SysprofSection *section; GtkListBoxRow *row; + GtkImage *image; + GtkLabel *label; + GtkBox *box; g_assert (GTK_IS_STACK_PAGE (page)); g_assert (SYSPROF_IS_SIDEBAR (sidebar)); section = SYSPROF_SECTION (gtk_stack_page_get_child (page)); + + box = g_object_new (GTK_TYPE_BOX, + "orientation", GTK_ORIENTATION_HORIZONTAL, + "spacing", 6, + NULL); + label = g_object_new (GTK_TYPE_LABEL, + "xalign", .0f, + "label", sysprof_section_get_title (section), + NULL); + image = g_object_new (GTK_TYPE_IMAGE, + "icon-name", sysprof_section_get_icon_name (section), + NULL); + gtk_box_append (box, GTK_WIDGET (image)); + gtk_box_append (box, GTK_WIDGET (label)); + row = g_object_new (GTK_TYPE_LIST_BOX_ROW, - "child", g_object_new (GTK_TYPE_LABEL, - "xalign", .0f, - "label", sysprof_section_get_title (section), - NULL), + "child", box, NULL); g_object_set_data_full (G_OBJECT (row), "SECTION", g_object_ref (section), g_object_unref); diff --git a/src/sysprof/sysprof-window.ui b/src/sysprof/sysprof-window.ui index c2d8d9b0..15e72680 100644 --- a/src/sysprof/sysprof-window.ui +++ b/src/sysprof/sysprof-window.ui @@ -100,6 +100,7 @@ callgraph + org.gnome.Sysprof-symbolic SysprofWindow @@ -116,6 +117,7 @@ processes + system-log-symbolic SysprofWindow @@ -124,6 +126,7 @@ processes + mark-chart-symbolic SysprofWindow @@ -132,6 +135,7 @@ processes + application-x-executable-symbolic SysprofWindow @@ -140,6 +144,7 @@ auxiliary + folder-symbolic SysprofWindow diff --git a/src/sysprof/sysprof.gresource.xml b/src/sysprof/sysprof.gresource.xml index 15d06dfb..cfc481c6 100644 --- a/src/sysprof/sysprof.gresource.xml +++ b/src/sysprof/sysprof.gresource.xml @@ -16,5 +16,6 @@ sysprof-window.ui icons/scalable/actions/mark-chart-symbolic.svg icons/scalable/actions/mark-table-symbolic.svg + icons/scalable/actions/system-log-symbolic.svg From 0be15aa897894539d7136a584f7ae77b008e92e3 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 10 Jul 2023 15:30:08 -0700 Subject: [PATCH 0819/1030] sysprof: add more section icons --- .../icons/scalable/actions/memory-allocations-symbolic.svg | 2 ++ src/sysprof/icons/scalable/actions/metadata-symbolic.svg | 1 + src/sysprof/sysprof-window.ui | 2 ++ src/sysprof/sysprof.gresource.xml | 2 ++ 4 files changed, 7 insertions(+) create mode 100644 src/sysprof/icons/scalable/actions/memory-allocations-symbolic.svg create mode 100644 src/sysprof/icons/scalable/actions/metadata-symbolic.svg diff --git a/src/sysprof/icons/scalable/actions/memory-allocations-symbolic.svg b/src/sysprof/icons/scalable/actions/memory-allocations-symbolic.svg new file mode 100644 index 00000000..4f1e184b --- /dev/null +++ b/src/sysprof/icons/scalable/actions/memory-allocations-symbolic.svg @@ -0,0 +1,2 @@ + + diff --git a/src/sysprof/icons/scalable/actions/metadata-symbolic.svg b/src/sysprof/icons/scalable/actions/metadata-symbolic.svg new file mode 100644 index 00000000..65d5d5d1 --- /dev/null +++ b/src/sysprof/icons/scalable/actions/metadata-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/sysprof/sysprof-window.ui b/src/sysprof/sysprof-window.ui index 15e72680..4ff12701 100644 --- a/src/sysprof/sysprof-window.ui +++ b/src/sysprof/sysprof-window.ui @@ -109,6 +109,7 @@ callgraph + memory-allocations-symbolic SysprofWindow @@ -153,6 +154,7 @@ auxiliary + metadata-symbolic SysprofWindow diff --git a/src/sysprof/sysprof.gresource.xml b/src/sysprof/sysprof.gresource.xml index cfc481c6..e6427a56 100644 --- a/src/sysprof/sysprof.gresource.xml +++ b/src/sysprof/sysprof.gresource.xml @@ -16,6 +16,8 @@ sysprof-window.ui icons/scalable/actions/mark-chart-symbolic.svg icons/scalable/actions/mark-table-symbolic.svg + icons/scalable/actions/memory-allocations-symbolic.svg + icons/scalable/actions/metadata-symbolic.svg icons/scalable/actions/system-log-symbolic.svg From 8c3fef768dff269f33e2c1ad7224f7e8e393a634 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 10 Jul 2023 16:18:42 -0700 Subject: [PATCH 0820/1030] libsysprof-analyze: expose properties for mmaps and mounts --- .../sysprof-document-process.c | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-document-process.c b/src/libsysprof-analyze/sysprof-document-process.c index d860bf57..02b18b38 100644 --- a/src/libsysprof-analyze/sysprof-document-process.c +++ b/src/libsysprof-analyze/sysprof-document-process.c @@ -41,6 +41,8 @@ enum { PROP_0, PROP_COMMAND_LINE, PROP_DURATION, + PROP_MEMORY_MAPS, + PROP_MOUNTS, PROP_EXIT_TIME, PROP_TITLE, N_PROPS @@ -82,6 +84,14 @@ sysprof_document_process_get_property (GObject *object, g_value_set_int64 (value, sysprof_document_process_get_exit_time (self)); break; + case PROP_MEMORY_MAPS: + g_value_take_object (value, sysprof_document_process_list_memory_maps (self)); + break; + + case PROP_MOUNTS: + g_value_take_object (value, sysprof_document_process_list_mounts (self)); + break; + case PROP_TITLE: g_value_take_string (value, sysprof_document_process_dup_title (self)); break; @@ -114,6 +124,16 @@ sysprof_document_process_class_init (SysprofDocumentProcessClass *klass) G_MININT64, G_MAXINT64, 0, (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + properties [PROP_MEMORY_MAPS] = + g_param_spec_object ("memory-maps", NULL, NULL, + G_TYPE_LIST_MODEL, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + properties [PROP_MOUNTS] = + g_param_spec_object ("mounts", NULL, NULL, + G_TYPE_LIST_MODEL, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + properties [PROP_TITLE] = g_param_spec_string ("title", NULL, NULL, NULL, From 812a54803c50938dad4e3539f836613fbb0e4587 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 10 Jul 2023 16:19:08 -0700 Subject: [PATCH 0821/1030] sysprof: add dialog to show process information Includes address layout and mount info for troubleshooting. --- .../actions/address-layout-symbolic.svg | 4 + .../actions/process-mounts-symbolic.svg | 4 + src/sysprof/meson.build | 1 + src/sysprof/sysprof-process-dialog.c | 142 +++++ src/sysprof/sysprof-process-dialog.h | 37 ++ src/sysprof/sysprof-process-dialog.ui | 572 ++++++++++++++++++ src/sysprof/sysprof-processes-section.c | 27 + src/sysprof/sysprof-processes-section.ui | 1 + src/sysprof/sysprof.gresource.xml | 3 + 9 files changed, 791 insertions(+) create mode 100644 src/sysprof/icons/scalable/actions/address-layout-symbolic.svg create mode 100644 src/sysprof/icons/scalable/actions/process-mounts-symbolic.svg create mode 100644 src/sysprof/sysprof-process-dialog.c create mode 100644 src/sysprof/sysprof-process-dialog.h create mode 100644 src/sysprof/sysprof-process-dialog.ui diff --git a/src/sysprof/icons/scalable/actions/address-layout-symbolic.svg b/src/sysprof/icons/scalable/actions/address-layout-symbolic.svg new file mode 100644 index 00000000..f0011c1b --- /dev/null +++ b/src/sysprof/icons/scalable/actions/address-layout-symbolic.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/sysprof/icons/scalable/actions/process-mounts-symbolic.svg b/src/sysprof/icons/scalable/actions/process-mounts-symbolic.svg new file mode 100644 index 00000000..1910fbab --- /dev/null +++ b/src/sysprof/icons/scalable/actions/process-mounts-symbolic.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/sysprof/meson.build b/src/sysprof/meson.build index 854b46ff..8fc49316 100644 --- a/src/sysprof/meson.build +++ b/src/sysprof/meson.build @@ -7,6 +7,7 @@ sysprof_sources = [ 'sysprof-marks-section.c', 'sysprof-memory-section.c', 'sysprof-metadata-section.c', + 'sysprof-process-dialog.c', 'sysprof-processes-section.c', 'sysprof-recording-pad.c', 'sysprof-samples-section.c', diff --git a/src/sysprof/sysprof-process-dialog.c b/src/sysprof/sysprof-process-dialog.c new file mode 100644 index 00000000..9296dd91 --- /dev/null +++ b/src/sysprof/sysprof-process-dialog.c @@ -0,0 +1,142 @@ +/* sysprof-process-dialog.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-process-dialog.h" + +struct _SysprofProcessDialog +{ + AdwWindow parent_instance; + SysprofDocumentProcess *process; +}; + +enum { + PROP_0, + PROP_PROCESS, + N_PROPS +}; + +G_DEFINE_FINAL_TYPE (SysprofProcessDialog, sysprof_process_dialog, ADW_TYPE_WINDOW) + +static GParamSpec *properties [N_PROPS]; + +static char * +format_address (gpointer unused, + SysprofAddress addr) +{ + return g_strdup_printf ("0x%"G_GINT64_MODIFIER"x", addr); +} + +static void +sysprof_process_dialog_dispose (GObject *object) +{ + SysprofProcessDialog *self = (SysprofProcessDialog *)object; + + gtk_widget_dispose_template (GTK_WIDGET (self), SYSPROF_TYPE_PROCESS_DIALOG); + + g_clear_object (&self->process); + + G_OBJECT_CLASS (sysprof_process_dialog_parent_class)->dispose (object); +} + +static void +sysprof_process_dialog_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofProcessDialog *self = SYSPROF_PROCESS_DIALOG (object); + + switch (prop_id) + { + case PROP_PROCESS: + g_value_set_object (value, sysprof_process_dialog_get_process (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_process_dialog_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SysprofProcessDialog *self = SYSPROF_PROCESS_DIALOG (object); + + switch (prop_id) + { + case PROP_PROCESS: + sysprof_process_dialog_set_process (self, g_value_get_object (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_process_dialog_class_init (SysprofProcessDialogClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->dispose = sysprof_process_dialog_dispose; + object_class->get_property = sysprof_process_dialog_get_property; + object_class->set_property = sysprof_process_dialog_set_property; + + properties [PROP_PROCESS] = + g_param_spec_object ("process", NULL, NULL, + SYSPROF_TYPE_DOCUMENT_PROCESS, + (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-process-dialog.ui"); + gtk_widget_class_bind_template_callback (widget_class, format_address); +} + +static void +sysprof_process_dialog_init (SysprofProcessDialog *self) +{ + gtk_widget_init_template (GTK_WIDGET (self)); +} + +SysprofDocumentProcess * +sysprof_process_dialog_get_process (SysprofProcessDialog *self) +{ + g_return_val_if_fail (SYSPROF_IS_PROCESS_DIALOG (self), NULL); + + return self->process; +} + +void +sysprof_process_dialog_set_process (SysprofProcessDialog *self, + SysprofDocumentProcess *process) +{ + g_return_if_fail (SYSPROF_IS_PROCESS_DIALOG (self)); + g_return_if_fail (!process || SYSPROF_IS_DOCUMENT_PROCESS (process)); + + if (g_set_object (&self->process, process)) + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_PROCESS]); +} diff --git a/src/sysprof/sysprof-process-dialog.h b/src/sysprof/sysprof-process-dialog.h new file mode 100644 index 00000000..fa1dc0cf --- /dev/null +++ b/src/sysprof/sysprof-process-dialog.h @@ -0,0 +1,37 @@ +/* sysprof-process-dialog.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +#include + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_PROCESS_DIALOG (sysprof_process_dialog_get_type()) + +G_DECLARE_FINAL_TYPE (SysprofProcessDialog, sysprof_process_dialog, SYSPROF, PROCESS_DIALOG, AdwWindow) + +SysprofDocumentProcess *sysprof_process_dialog_get_process (SysprofProcessDialog *self); +void sysprof_process_dialog_set_process (SysprofProcessDialog *self, + SysprofDocumentProcess *process); + +G_END_DECLS diff --git a/src/sysprof/sysprof-process-dialog.ui b/src/sysprof/sysprof-process-dialog.ui new file mode 100644 index 00000000..5b660a71 --- /dev/null +++ b/src/sysprof/sysprof-process-dialog.ui @@ -0,0 +1,572 @@ + + + + diff --git a/src/sysprof/sysprof-processes-section.c b/src/sysprof/sysprof-processes-section.c index 8a57b309..75dbf557 100644 --- a/src/sysprof/sysprof-processes-section.c +++ b/src/sysprof/sysprof-processes-section.c @@ -25,6 +25,7 @@ #include #include +#include "sysprof-process-dialog.h" #include "sysprof-processes-section.h" #include "sysprof-single-model.h" @@ -37,6 +38,31 @@ struct _SysprofProcessesSection G_DEFINE_FINAL_TYPE (SysprofProcessesSection, sysprof_processes_section, SYSPROF_TYPE_SECTION) +static void +activate_layer_item_cb (GtkListItem *list_item, + SysprofChartLayer *layer, + SysprofDocumentProcess *process, + SysprofChart *chart) +{ + SysprofProcessDialog *dialog; + g_autofree char *title = NULL; + + g_assert (GTK_IS_LIST_ITEM (list_item)); + g_assert (SYSPROF_IS_CHART_LAYER (layer)); + g_assert (SYSPROF_IS_DOCUMENT_PROCESS (process)); + g_assert (SYSPROF_IS_CHART (chart)); + + title = sysprof_document_process_dup_title (process); + + dialog = g_object_new (SYSPROF_TYPE_PROCESS_DIALOG, + "process", process, + "transient-for", gtk_widget_get_root (GTK_WIDGET (chart)), + "title", title, + NULL); + + gtk_window_present (GTK_WINDOW (dialog)); +} + static void sysprof_processes_section_dispose (GObject *object) { @@ -57,6 +83,7 @@ sysprof_processes_section_class_init (SysprofProcessesSectionClass *klass) gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/sysprof/sysprof-processes-section.ui"); gtk_widget_class_bind_template_child (widget_class, SysprofProcessesSection, list_view); + gtk_widget_class_bind_template_callback (widget_class, activate_layer_item_cb); g_type_ensure (SYSPROF_TYPE_CHART); g_type_ensure (SYSPROF_TYPE_CHART_LAYER); diff --git a/src/sysprof/sysprof-processes-section.ui b/src/sysprof/sysprof-processes-section.ui index 3a9bdeb6..420c72bf 100644 --- a/src/sysprof/sysprof-processes-section.ui +++ b/src/sysprof/sysprof-processes-section.ui @@ -42,6 +42,7 @@ 16 + true diff --git a/src/sysprof/sysprof.gresource.xml b/src/sysprof/sysprof.gresource.xml index e6427a56..659d79bc 100644 --- a/src/sysprof/sysprof.gresource.xml +++ b/src/sysprof/sysprof.gresource.xml @@ -9,15 +9,18 @@ sysprof-marks-section.ui sysprof-memory-section.ui sysprof-metadata-section.ui + sysprof-process-dialog.ui sysprof-processes-section.ui sysprof-recording-pad.ui sysprof-samples-section.ui sysprof-sidebar.ui sysprof-window.ui + icons/scalable/actions/address-layout-symbolic.svg icons/scalable/actions/mark-chart-symbolic.svg icons/scalable/actions/mark-table-symbolic.svg icons/scalable/actions/memory-allocations-symbolic.svg icons/scalable/actions/metadata-symbolic.svg + icons/scalable/actions/process-mounts-symbolic.svg icons/scalable/actions/system-log-symbolic.svg From 6075a0cd91db19b089b81fe9552bfe19608e8f5a Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 11 Jul 2023 13:32:09 -0700 Subject: [PATCH 0822/1030] build: fix log domains --- src/libsysprof-analyze/tests/meson.build | 2 +- src/libsysprof-gtk/tests/meson.build | 2 +- src/libsysprof-profile/tests/meson.build | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libsysprof-analyze/tests/meson.build b/src/libsysprof-analyze/tests/meson.build index e0b88332..8f73e931 100644 --- a/src/libsysprof-analyze/tests/meson.build +++ b/src/libsysprof-analyze/tests/meson.build @@ -5,7 +5,7 @@ libsysprof_analyze_test_env = [ ] libsysprof_analyze_testsuite_c_args = [ - '-DG_LOG_DOMAIN="libdex"', + '-DG_LOG_DOMAIN="libsysprof-analyze"', '-DG_ENABLE_DEBUG', '-UG_DISABLE_ASSERT', '-UG_DISABLE_CAST_CHECKS', diff --git a/src/libsysprof-gtk/tests/meson.build b/src/libsysprof-gtk/tests/meson.build index 106f9b0e..2656da1a 100644 --- a/src/libsysprof-gtk/tests/meson.build +++ b/src/libsysprof-gtk/tests/meson.build @@ -5,7 +5,7 @@ libsysprof_gtk_test_env = [ ] libsysprof_gtk_testsuite_c_args = [ - '-DG_LOG_DOMAIN="libdex"', + '-DG_LOG_DOMAIN="libsysprof-gtk"', '-DG_ENABLE_DEBUG', '-UG_DISABLE_ASSERT', '-UG_DISABLE_CAST_CHECKS', diff --git a/src/libsysprof-profile/tests/meson.build b/src/libsysprof-profile/tests/meson.build index f6b5d697..48b7343f 100644 --- a/src/libsysprof-profile/tests/meson.build +++ b/src/libsysprof-profile/tests/meson.build @@ -5,7 +5,7 @@ libsysprof_profile_test_env = [ ] libsysprof_profile_testsuite_c_args = [ - '-DG_LOG_DOMAIN="libdex"', + '-DG_LOG_DOMAIN="libsysprof-profile"', '-DG_ENABLE_DEBUG', '-UG_DISABLE_ASSERT', '-UG_DISABLE_CAST_CHECKS', From ae571f3f6e3332e3353b9a833eb1097ebe5d427e Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 12 Jul 2023 10:00:11 -0700 Subject: [PATCH 0823/1030] libsysprof-ui: remove legacy libsysprof-ui library This is libsysprof-gtk now, but that too will be absorbed by sysprof app directly rather than having a UI library. --- .../css/SysprofDisplay-shared.css | 86 - .../css/SysprofEnvironEditor-shared.css | 13 - .../css/SysprofProfilerAssistant-shared.css | 7 - src/libsysprof-ui/egg-handle-private.h | 35 - src/libsysprof-ui/egg-handle.c | 145 -- src/libsysprof-ui/egg-paned-private.h | 48 - src/libsysprof-ui/egg-paned.c | 593 ------- src/libsysprof-ui/egg-resizer-private.h | 40 - src/libsysprof-ui/egg-resizer.c | 495 ------ src/libsysprof-ui/libsysprof-ui.gresource.xml | 30 - src/libsysprof-ui/meson.build | 136 -- src/libsysprof-ui/pointcache.c | 116 -- src/libsysprof-ui/pointcache.h | 52 - src/libsysprof-ui/rectangles.c | 248 --- src/libsysprof-ui/rectangles.h | 48 - src/libsysprof-ui/sysprof-aid-icon.c | 211 --- src/libsysprof-ui/sysprof-aid-icon.h | 39 - src/libsysprof-ui/sysprof-aid-icon.ui | 41 - src/libsysprof-ui/sysprof-aid.c | 353 ---- src/libsysprof-ui/sysprof-aid.h | 76 - src/libsysprof-ui/sysprof-battery-aid.c | 242 --- src/libsysprof-ui/sysprof-battery-aid.h | 33 - src/libsysprof-ui/sysprof-cairo.c | 71 - src/libsysprof-ui/sysprof-callgraph-aid.c | 275 --- src/libsysprof-ui/sysprof-callgraph-aid.h | 33 - src/libsysprof-ui/sysprof-callgraph-page.c | 1299 -------------- src/libsysprof-ui/sysprof-callgraph-page.h | 51 - src/libsysprof-ui/sysprof-callgraph-page.ui | 212 --- .../sysprof-cell-renderer-duration.c | 455 ----- .../sysprof-cell-renderer-duration.h | 41 - .../sysprof-cell-renderer-percent.c | 139 -- .../sysprof-cell-renderer-percent.h | 57 - .../sysprof-cell-renderer-progress.c | 712 -------- .../sysprof-cell-renderer-progress.h | 53 - src/libsysprof-ui/sysprof-check.c | 106 -- src/libsysprof-ui/sysprof-check.h | 37 - src/libsysprof-ui/sysprof-color-cycle.c | 157 -- src/libsysprof-ui/sysprof-color-cycle.h | 41 - src/libsysprof-ui/sysprof-counters-aid.c | 284 --- src/libsysprof-ui/sysprof-counters-aid.h | 33 - src/libsysprof-ui/sysprof-cpu-aid.c | 357 ---- src/libsysprof-ui/sysprof-cpu-aid.h | 33 - src/libsysprof-ui/sysprof-depth-visualizer.c | 453 ----- src/libsysprof-ui/sysprof-depth-visualizer.h | 40 - src/libsysprof-ui/sysprof-details-page.c | 325 ---- src/libsysprof-ui/sysprof-details-page.h | 57 - src/libsysprof-ui/sysprof-details-page.ui | 361 ---- src/libsysprof-ui/sysprof-diskstat-aid.c | 269 --- src/libsysprof-ui/sysprof-diskstat-aid.h | 33 - src/libsysprof-ui/sysprof-display-private.h | 29 - src/libsysprof-ui/sysprof-display.c | 1340 -------------- src/libsysprof-ui/sysprof-display.h | 96 - src/libsysprof-ui/sysprof-display.ui | 71 - src/libsysprof-ui/sysprof-duplex-visualizer.c | 633 ------- src/libsysprof-ui/sysprof-duplex-visualizer.h | 45 - .../sysprof-environ-editor-row.c | 277 --- .../sysprof-environ-editor-row.h | 38 - .../sysprof-environ-editor-row.ui | 40 - src/libsysprof-ui/sysprof-environ-editor.c | 343 ---- src/libsysprof-ui/sysprof-environ-editor.h | 38 - src/libsysprof-ui/sysprof-environ-variable.c | 185 -- src/libsysprof-ui/sysprof-environ-variable.h | 40 - src/libsysprof-ui/sysprof-environ.c | 379 ---- src/libsysprof-ui/sysprof-environ.h | 52 - src/libsysprof-ui/sysprof-failed-state-view.c | 62 - src/libsysprof-ui/sysprof-failed-state-view.h | 41 - .../sysprof-failed-state-view.ui | 47 - src/libsysprof-ui/sysprof-line-visualizer.c | 909 ---------- src/libsysprof-ui/sysprof-line-visualizer.h | 57 - src/libsysprof-ui/sysprof-log-model.c | 422 ----- src/libsysprof-ui/sysprof-log-model.h | 49 - src/libsysprof-ui/sysprof-logs-aid.c | 237 --- src/libsysprof-ui/sysprof-logs-aid.h | 33 - src/libsysprof-ui/sysprof-logs-page.c | 116 -- src/libsysprof-ui/sysprof-logs-page.h | 31 - src/libsysprof-ui/sysprof-logs-page.ui | 73 - src/libsysprof-ui/sysprof-mark-detail.c | 208 --- src/libsysprof-ui/sysprof-mark-detail.h | 37 - src/libsysprof-ui/sysprof-mark-visualizer.c | 276 --- src/libsysprof-ui/sysprof-mark-visualizer.h | 47 - src/libsysprof-ui/sysprof-marks-aid.c | 490 ------ src/libsysprof-ui/sysprof-marks-aid.h | 33 - src/libsysprof-ui/sysprof-marks-model.c | 592 ------- src/libsysprof-ui/sysprof-marks-model.h | 65 - src/libsysprof-ui/sysprof-marks-page.c | 610 ------- src/libsysprof-ui/sysprof-marks-page.h | 44 - src/libsysprof-ui/sysprof-marks-page.ui | 259 --- src/libsysprof-ui/sysprof-memory-aid.c | 70 - src/libsysprof-ui/sysprof-memory-aid.h | 33 - src/libsysprof-ui/sysprof-memprof-aid.c | 226 --- src/libsysprof-ui/sysprof-memprof-aid.h | 33 - src/libsysprof-ui/sysprof-memprof-page.c | 1552 ----------------- src/libsysprof-ui/sysprof-memprof-page.h | 51 - src/libsysprof-ui/sysprof-memprof-page.ui | 330 ---- .../sysprof-memprof-visualizer.c | 613 ------- .../sysprof-memprof-visualizer.h | 33 - src/libsysprof-ui/sysprof-model-filter.c | 497 ------ src/libsysprof-ui/sysprof-model-filter.h | 60 - src/libsysprof-ui/sysprof-netdev-aid.c | 266 --- src/libsysprof-ui/sysprof-netdev-aid.h | 33 - src/libsysprof-ui/sysprof-notebook.c | 561 ------ src/libsysprof-ui/sysprof-notebook.h | 85 - src/libsysprof-ui/sysprof-page.c | 256 --- src/libsysprof-ui/sysprof-page.h | 88 - src/libsysprof-ui/sysprof-process-model-row.c | 258 --- src/libsysprof-ui/sysprof-process-model-row.h | 54 - .../sysprof-process-model-row.ui | 52 - src/libsysprof-ui/sysprof-procs-visualizer.c | 296 ---- src/libsysprof-ui/sysprof-procs-visualizer.h | 33 - .../sysprof-profiler-assistant.c | 493 ------ .../sysprof-profiler-assistant.h | 37 - .../sysprof-profiler-assistant.ui | 279 --- src/libsysprof-ui/sysprof-proxy-aid.c | 209 --- src/libsysprof-ui/sysprof-proxy-aid.h | 46 - src/libsysprof-ui/sysprof-rapl-aid.c | 253 --- src/libsysprof-ui/sysprof-rapl-aid.h | 33 - .../sysprof-recording-state-view.c | 202 --- .../sysprof-recording-state-view.h | 41 - .../sysprof-recording-state-view.ui | 83 - src/libsysprof-ui/sysprof-scrollmap.c | 333 ---- src/libsysprof-ui/sysprof-scrollmap.h | 40 - src/libsysprof-ui/sysprof-tab.c | 158 -- src/libsysprof-ui/sysprof-tab.h | 35 - src/libsysprof-ui/sysprof-tab.ui | 36 - src/libsysprof-ui/sysprof-theme-manager.c | 269 --- src/libsysprof-ui/sysprof-theme-manager.h | 47 - src/libsysprof-ui/sysprof-time-label.c | 117 -- src/libsysprof-ui/sysprof-time-label.h | 34 - src/libsysprof-ui/sysprof-time-visualizer.c | 543 ------ src/libsysprof-ui/sysprof-time-visualizer.h | 54 - src/libsysprof-ui/sysprof-ui-private.h | 48 - src/libsysprof-ui/sysprof-ui.h | 41 - .../sysprof-visualizer-group-header.c | 241 --- .../sysprof-visualizer-group-header.h | 31 - .../sysprof-visualizer-group-private.h | 45 - src/libsysprof-ui/sysprof-visualizer-group.c | 471 ----- src/libsysprof-ui/sysprof-visualizer-group.h | 76 - src/libsysprof-ui/sysprof-visualizer-ticks.c | 324 ---- src/libsysprof-ui/sysprof-visualizer-ticks.h | 35 - src/libsysprof-ui/sysprof-visualizer.c | 289 --- src/libsysprof-ui/sysprof-visualizer.h | 88 - src/libsysprof-ui/sysprof-visualizers-frame.c | 721 -------- src/libsysprof-ui/sysprof-visualizers-frame.h | 52 - .../sysprof-visualizers-frame.ui | 242 --- src/libsysprof-ui/sysprof-zoom-manager.c | 661 ------- src/libsysprof-ui/sysprof-zoom-manager.h | 59 - src/meson.build | 1 - src/tests/meson.build | 33 - 148 files changed, 29695 deletions(-) delete mode 100644 src/libsysprof-ui/css/SysprofDisplay-shared.css delete mode 100644 src/libsysprof-ui/css/SysprofEnvironEditor-shared.css delete mode 100644 src/libsysprof-ui/css/SysprofProfilerAssistant-shared.css delete mode 100644 src/libsysprof-ui/egg-handle-private.h delete mode 100644 src/libsysprof-ui/egg-handle.c delete mode 100644 src/libsysprof-ui/egg-paned-private.h delete mode 100644 src/libsysprof-ui/egg-paned.c delete mode 100644 src/libsysprof-ui/egg-resizer-private.h delete mode 100644 src/libsysprof-ui/egg-resizer.c delete mode 100644 src/libsysprof-ui/libsysprof-ui.gresource.xml delete mode 100644 src/libsysprof-ui/meson.build delete mode 100644 src/libsysprof-ui/pointcache.c delete mode 100644 src/libsysprof-ui/pointcache.h delete mode 100644 src/libsysprof-ui/rectangles.c delete mode 100644 src/libsysprof-ui/rectangles.h delete mode 100644 src/libsysprof-ui/sysprof-aid-icon.c delete mode 100644 src/libsysprof-ui/sysprof-aid-icon.h delete mode 100644 src/libsysprof-ui/sysprof-aid-icon.ui delete mode 100644 src/libsysprof-ui/sysprof-aid.c delete mode 100644 src/libsysprof-ui/sysprof-aid.h delete mode 100644 src/libsysprof-ui/sysprof-battery-aid.c delete mode 100644 src/libsysprof-ui/sysprof-battery-aid.h delete mode 100644 src/libsysprof-ui/sysprof-cairo.c delete mode 100644 src/libsysprof-ui/sysprof-callgraph-aid.c delete mode 100644 src/libsysprof-ui/sysprof-callgraph-aid.h delete mode 100644 src/libsysprof-ui/sysprof-callgraph-page.c delete mode 100644 src/libsysprof-ui/sysprof-callgraph-page.h delete mode 100644 src/libsysprof-ui/sysprof-callgraph-page.ui delete mode 100644 src/libsysprof-ui/sysprof-cell-renderer-duration.c delete mode 100644 src/libsysprof-ui/sysprof-cell-renderer-duration.h delete mode 100644 src/libsysprof-ui/sysprof-cell-renderer-percent.c delete mode 100644 src/libsysprof-ui/sysprof-cell-renderer-percent.h delete mode 100644 src/libsysprof-ui/sysprof-cell-renderer-progress.c delete mode 100644 src/libsysprof-ui/sysprof-cell-renderer-progress.h delete mode 100644 src/libsysprof-ui/sysprof-check.c delete mode 100644 src/libsysprof-ui/sysprof-check.h delete mode 100644 src/libsysprof-ui/sysprof-color-cycle.c delete mode 100644 src/libsysprof-ui/sysprof-color-cycle.h delete mode 100644 src/libsysprof-ui/sysprof-counters-aid.c delete mode 100644 src/libsysprof-ui/sysprof-counters-aid.h delete mode 100644 src/libsysprof-ui/sysprof-cpu-aid.c delete mode 100644 src/libsysprof-ui/sysprof-cpu-aid.h delete mode 100644 src/libsysprof-ui/sysprof-depth-visualizer.c delete mode 100644 src/libsysprof-ui/sysprof-depth-visualizer.h delete mode 100644 src/libsysprof-ui/sysprof-details-page.c delete mode 100644 src/libsysprof-ui/sysprof-details-page.h delete mode 100644 src/libsysprof-ui/sysprof-details-page.ui delete mode 100644 src/libsysprof-ui/sysprof-diskstat-aid.c delete mode 100644 src/libsysprof-ui/sysprof-diskstat-aid.h delete mode 100644 src/libsysprof-ui/sysprof-display-private.h delete mode 100644 src/libsysprof-ui/sysprof-display.c delete mode 100644 src/libsysprof-ui/sysprof-display.h delete mode 100644 src/libsysprof-ui/sysprof-display.ui delete mode 100644 src/libsysprof-ui/sysprof-duplex-visualizer.c delete mode 100644 src/libsysprof-ui/sysprof-duplex-visualizer.h delete mode 100644 src/libsysprof-ui/sysprof-environ-editor-row.c delete mode 100644 src/libsysprof-ui/sysprof-environ-editor-row.h delete mode 100644 src/libsysprof-ui/sysprof-environ-editor-row.ui delete mode 100644 src/libsysprof-ui/sysprof-environ-editor.c delete mode 100644 src/libsysprof-ui/sysprof-environ-editor.h delete mode 100644 src/libsysprof-ui/sysprof-environ-variable.c delete mode 100644 src/libsysprof-ui/sysprof-environ-variable.h delete mode 100644 src/libsysprof-ui/sysprof-environ.c delete mode 100644 src/libsysprof-ui/sysprof-environ.h delete mode 100644 src/libsysprof-ui/sysprof-failed-state-view.c delete mode 100644 src/libsysprof-ui/sysprof-failed-state-view.h delete mode 100644 src/libsysprof-ui/sysprof-failed-state-view.ui delete mode 100644 src/libsysprof-ui/sysprof-line-visualizer.c delete mode 100644 src/libsysprof-ui/sysprof-line-visualizer.h delete mode 100644 src/libsysprof-ui/sysprof-log-model.c delete mode 100644 src/libsysprof-ui/sysprof-log-model.h delete mode 100644 src/libsysprof-ui/sysprof-logs-aid.c delete mode 100644 src/libsysprof-ui/sysprof-logs-aid.h delete mode 100644 src/libsysprof-ui/sysprof-logs-page.c delete mode 100644 src/libsysprof-ui/sysprof-logs-page.h delete mode 100644 src/libsysprof-ui/sysprof-logs-page.ui delete mode 100644 src/libsysprof-ui/sysprof-mark-detail.c delete mode 100644 src/libsysprof-ui/sysprof-mark-detail.h delete mode 100644 src/libsysprof-ui/sysprof-mark-visualizer.c delete mode 100644 src/libsysprof-ui/sysprof-mark-visualizer.h delete mode 100644 src/libsysprof-ui/sysprof-marks-aid.c delete mode 100644 src/libsysprof-ui/sysprof-marks-aid.h delete mode 100644 src/libsysprof-ui/sysprof-marks-model.c delete mode 100644 src/libsysprof-ui/sysprof-marks-model.h delete mode 100644 src/libsysprof-ui/sysprof-marks-page.c delete mode 100644 src/libsysprof-ui/sysprof-marks-page.h delete mode 100644 src/libsysprof-ui/sysprof-marks-page.ui delete mode 100644 src/libsysprof-ui/sysprof-memory-aid.c delete mode 100644 src/libsysprof-ui/sysprof-memory-aid.h delete mode 100644 src/libsysprof-ui/sysprof-memprof-aid.c delete mode 100644 src/libsysprof-ui/sysprof-memprof-aid.h delete mode 100644 src/libsysprof-ui/sysprof-memprof-page.c delete mode 100644 src/libsysprof-ui/sysprof-memprof-page.h delete mode 100644 src/libsysprof-ui/sysprof-memprof-page.ui delete mode 100644 src/libsysprof-ui/sysprof-memprof-visualizer.c delete mode 100644 src/libsysprof-ui/sysprof-memprof-visualizer.h delete mode 100644 src/libsysprof-ui/sysprof-model-filter.c delete mode 100644 src/libsysprof-ui/sysprof-model-filter.h delete mode 100644 src/libsysprof-ui/sysprof-netdev-aid.c delete mode 100644 src/libsysprof-ui/sysprof-netdev-aid.h delete mode 100644 src/libsysprof-ui/sysprof-notebook.c delete mode 100644 src/libsysprof-ui/sysprof-notebook.h delete mode 100644 src/libsysprof-ui/sysprof-page.c delete mode 100644 src/libsysprof-ui/sysprof-page.h delete mode 100644 src/libsysprof-ui/sysprof-process-model-row.c delete mode 100644 src/libsysprof-ui/sysprof-process-model-row.h delete mode 100644 src/libsysprof-ui/sysprof-process-model-row.ui delete mode 100644 src/libsysprof-ui/sysprof-procs-visualizer.c delete mode 100644 src/libsysprof-ui/sysprof-procs-visualizer.h delete mode 100644 src/libsysprof-ui/sysprof-profiler-assistant.c delete mode 100644 src/libsysprof-ui/sysprof-profiler-assistant.h delete mode 100644 src/libsysprof-ui/sysprof-profiler-assistant.ui delete mode 100644 src/libsysprof-ui/sysprof-proxy-aid.c delete mode 100644 src/libsysprof-ui/sysprof-proxy-aid.h delete mode 100644 src/libsysprof-ui/sysprof-rapl-aid.c delete mode 100644 src/libsysprof-ui/sysprof-rapl-aid.h delete mode 100644 src/libsysprof-ui/sysprof-recording-state-view.c delete mode 100644 src/libsysprof-ui/sysprof-recording-state-view.h delete mode 100644 src/libsysprof-ui/sysprof-recording-state-view.ui delete mode 100644 src/libsysprof-ui/sysprof-scrollmap.c delete mode 100644 src/libsysprof-ui/sysprof-scrollmap.h delete mode 100644 src/libsysprof-ui/sysprof-tab.c delete mode 100644 src/libsysprof-ui/sysprof-tab.h delete mode 100644 src/libsysprof-ui/sysprof-tab.ui delete mode 100644 src/libsysprof-ui/sysprof-theme-manager.c delete mode 100644 src/libsysprof-ui/sysprof-theme-manager.h delete mode 100644 src/libsysprof-ui/sysprof-time-label.c delete mode 100644 src/libsysprof-ui/sysprof-time-label.h delete mode 100644 src/libsysprof-ui/sysprof-time-visualizer.c delete mode 100644 src/libsysprof-ui/sysprof-time-visualizer.h delete mode 100644 src/libsysprof-ui/sysprof-ui-private.h delete mode 100644 src/libsysprof-ui/sysprof-ui.h delete mode 100644 src/libsysprof-ui/sysprof-visualizer-group-header.c delete mode 100644 src/libsysprof-ui/sysprof-visualizer-group-header.h delete mode 100644 src/libsysprof-ui/sysprof-visualizer-group-private.h delete mode 100644 src/libsysprof-ui/sysprof-visualizer-group.c delete mode 100644 src/libsysprof-ui/sysprof-visualizer-group.h delete mode 100644 src/libsysprof-ui/sysprof-visualizer-ticks.c delete mode 100644 src/libsysprof-ui/sysprof-visualizer-ticks.h delete mode 100644 src/libsysprof-ui/sysprof-visualizer.c delete mode 100644 src/libsysprof-ui/sysprof-visualizer.h delete mode 100644 src/libsysprof-ui/sysprof-visualizers-frame.c delete mode 100644 src/libsysprof-ui/sysprof-visualizers-frame.h delete mode 100644 src/libsysprof-ui/sysprof-visualizers-frame.ui delete mode 100644 src/libsysprof-ui/sysprof-zoom-manager.c delete mode 100644 src/libsysprof-ui/sysprof-zoom-manager.h diff --git a/src/libsysprof-ui/css/SysprofDisplay-shared.css b/src/libsysprof-ui/css/SysprofDisplay-shared.css deleted file mode 100644 index a4ca6d57..00000000 --- a/src/libsysprof-ui/css/SysprofDisplay-shared.css +++ /dev/null @@ -1,86 +0,0 @@ -SysprofVisualizer { - background: @content_view_bg; - } -SysprofVisualizer:not(:last-child) { - border-bottom: 1px solid alpha(@borders, 0.3); - } - -SysprofVisualizerGroup { - border-bottom: 1px solid @borders; - } -SysprofVisualizerGroup:last-child { - box-shadow: 0 20px 15px 15px alpha(@borders, 0.3); - } - -SysprofVisualizersFrame box.horizontal.inline-toolbar { - padding: 0; - margin: 0; - border: none; - border-radius: 0; - border-width: 0; - } - -SysprofVisualizersFrame viewport.visualizers { - border-right: 1px solid @borders; - box-shadow: 1px 1px 3px alpha(@borders, 0.5); - } - -SysprofVisualizersFrame scrollbar.horizontal { - color: mix(@theme_fg_color, @theme_selected_bg_color, 0.5); - background: transparent; - } - -SysprofVisualizersFrame scrollbar.horizontal range trough { - background: transparent; - } - -SysprofVisualizersFrame scrollbar.horizontal range trough slider { - margin-left: 1px; - margin-right: 1px; - padding: 6px; - min-height: 14px; - background: alpha(@content_view_bg, 0.2); - border-radius: 3px; - border: 2px solid @theme_selected_bg_color; - box-shadow: inset 0 10px 5px alpha(@content_view_bg,.3), - inset 0 -15px 5px alpha(@content_view_bg,.1), - 0px 2px 4px @borders; - } - -SysprofVisualizerTicks { - color: mix(@theme_fg_color, @borders, 0.5); - background-color: @content_view_bg; - } - -SysprofVisualizersFrame list { - background-color: @theme_bg_color; - } - -SysprofVisualizersFrame list.visualizer-groups row { - padding: 0; - border-bottom: 1px solid @borders; - } -SysprofVisualizersFrame list.visualizer-groups row:not(:selected) { - background-color: @theme_bg_color; - } -SysprofVisualizersFrame list.visualizer-groups row:last-child { - box-shadow: 0 20px 15px 15px alpha(@borders, 0.3); - } - -SysprofVisualizersFrame .left-column .small-button.flat { - border-color: transparent; - min-height: 8px; - min-width: 8px; - } -SysprofVisualizersFrame .left-column .small-button.flat:checked, -SysprofVisualizersFrame .left-column .small-button.flat:hover { - border-color: @borders; - } - -SysprofVisualizersFrame .selection { - border-radius: 3px; - background-color: alpha(@theme_selected_bg_color, 0.35); - box-shadow: inset 0 10px 5px alpha(@content_view_bg,.3), - inset 0 -15px 5px alpha(@content_view_bg,.1), - inset 0 0 1px 1px @theme_selected_bg_color; - } diff --git a/src/libsysprof-ui/css/SysprofEnvironEditor-shared.css b/src/libsysprof-ui/css/SysprofEnvironEditor-shared.css deleted file mode 100644 index e202b29b..00000000 --- a/src/libsysprof-ui/css/SysprofEnvironEditor-shared.css +++ /dev/null @@ -1,13 +0,0 @@ -list.environ-editor row button.flat:last-child { - min-height: 16px; - min-width: 16px; - padding: 0; - margin: 3px; -} -list.environ-editor row button.flat:not(:hover) { - border-color: transparent; -} -list.environ-editor row button.flat image { - padding: 3px; - margin: 0; -} diff --git a/src/libsysprof-ui/css/SysprofProfilerAssistant-shared.css b/src/libsysprof-ui/css/SysprofProfilerAssistant-shared.css deleted file mode 100644 index f0acc7e4..00000000 --- a/src/libsysprof-ui/css/SysprofProfilerAssistant-shared.css +++ /dev/null @@ -1,7 +0,0 @@ -sysprofaidicon image.right.top { - border-radius: 9999px; - background-color: @theme_selected_bg_color; - color: @theme_selected_fg_color; - padding: 2px; - margin: 0px; -} diff --git a/src/libsysprof-ui/egg-handle-private.h b/src/libsysprof-ui/egg-handle-private.h deleted file mode 100644 index 9c004aa0..00000000 --- a/src/libsysprof-ui/egg-handle-private.h +++ /dev/null @@ -1,35 +0,0 @@ -/* egg-handle.h - * - * Copyright 2021 Christian Hergert - * - * This file 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 3 of the License, or (at your option) - * any later version. - * - * This file 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 . - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ - -#pragma once - -#include - -G_BEGIN_DECLS - -#define EGG_TYPE_HANDLE (egg_handle_get_type()) - -G_DECLARE_FINAL_TYPE (EggHandle, egg_handle, EGG, HANDLE, GtkWidget) - -GtkWidget *egg_handle_new (GtkPositionType position); -void egg_handle_set_position (EggHandle *self, - GtkPositionType position); - -G_END_DECLS diff --git a/src/libsysprof-ui/egg-handle.c b/src/libsysprof-ui/egg-handle.c deleted file mode 100644 index 5872abb3..00000000 --- a/src/libsysprof-ui/egg-handle.c +++ /dev/null @@ -1,145 +0,0 @@ -/* egg-handle.c - * - * Copyright 2021 Christian Hergert - * - * This file 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 3 of the License, or (at your option) - * any later version. - * - * This file 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 . - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ - -#include "config.h" - -#include "egg-handle-private.h" - -#define EXTRA_SIZE 8 - -struct _EggHandle -{ - GtkWidget parent_instance; - GtkWidget *separator; - GtkPositionType position : 3; -}; - -G_DEFINE_TYPE (EggHandle, egg_handle, GTK_TYPE_WIDGET) - -static gboolean -egg_handle_contains (GtkWidget *widget, - double x, - double y) -{ - EggHandle *self = (EggHandle *)widget; - graphene_rect_t area; - - g_assert (EGG_IS_HANDLE (self)); - - if (!gtk_widget_compute_bounds (GTK_WIDGET (self->separator), - GTK_WIDGET (self), - &area)) - return FALSE; - - switch (self->position) - { - case GTK_POS_LEFT: - area.origin.x -= EXTRA_SIZE; - area.size.width = EXTRA_SIZE; - break; - - case GTK_POS_RIGHT: - area.size.width = EXTRA_SIZE; - break; - - case GTK_POS_TOP: - area.origin.y -= EXTRA_SIZE; - area.size.height = EXTRA_SIZE; - break; - - case GTK_POS_BOTTOM: - area.size.height = EXTRA_SIZE; - break; - - default: - g_assert_not_reached (); - break; - } - - return graphene_rect_contains_point (&area, &GRAPHENE_POINT_INIT (x, y)); -} - -static void -egg_handle_dispose (GObject *object) -{ - EggHandle *self = (EggHandle *)object; - - g_clear_pointer (&self->separator, gtk_widget_unparent); - - G_OBJECT_CLASS (egg_handle_parent_class)->dispose (object); -} - -static void -egg_handle_class_init (EggHandleClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); - - object_class->dispose = egg_handle_dispose; - - widget_class->contains = egg_handle_contains; - - gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT); -} - -static void -egg_handle_init (EggHandle *self) -{ - self->separator = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL); - gtk_widget_set_parent (GTK_WIDGET (self->separator), GTK_WIDGET (self)); -} - -void -egg_handle_set_position (EggHandle *self, - GtkPositionType position) -{ - g_return_if_fail (EGG_IS_HANDLE (self)); - - self->position = position; - - switch (position) - { - case GTK_POS_LEFT: - case GTK_POS_RIGHT: - gtk_widget_set_cursor_from_name (GTK_WIDGET (self), "col-resize"); - gtk_orientable_set_orientation (GTK_ORIENTABLE (self->separator), GTK_ORIENTATION_VERTICAL); - break; - - case GTK_POS_TOP: - case GTK_POS_BOTTOM: - gtk_widget_set_cursor_from_name (GTK_WIDGET (self), "row-resize"); - gtk_orientable_set_orientation (GTK_ORIENTABLE (self->separator), GTK_ORIENTATION_HORIZONTAL); - break; - - default: - g_assert_not_reached (); - } -} - -GtkWidget * -egg_handle_new (GtkPositionType position) -{ - EggHandle *self; - - self = g_object_new (EGG_TYPE_HANDLE, NULL); - egg_handle_set_position (self, position); - - return GTK_WIDGET (self); -} diff --git a/src/libsysprof-ui/egg-paned-private.h b/src/libsysprof-ui/egg-paned-private.h deleted file mode 100644 index b673c1a4..00000000 --- a/src/libsysprof-ui/egg-paned-private.h +++ /dev/null @@ -1,48 +0,0 @@ -/* egg-paned.h - * - * Copyright 2021 Christian Hergert - * - * This file 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 3 of the License, or (at your option) - * any later version. - * - * This file 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 . - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ - -#pragma once - -#include - -G_BEGIN_DECLS - -#define EGG_TYPE_PANED (egg_paned_get_type()) - -G_DECLARE_FINAL_TYPE (EggPaned, egg_paned, EGG, PANED, GtkWidget) - -GtkWidget *egg_paned_new (void); -void egg_paned_append (EggPaned *self, - GtkWidget *child); -void egg_paned_prepend (EggPaned *self, - GtkWidget *child); -void egg_paned_insert (EggPaned *self, - int position, - GtkWidget *child); -void egg_paned_insert_after (EggPaned *self, - GtkWidget *child, - GtkWidget *sibling); -void egg_paned_remove (EggPaned *self, - GtkWidget *child); -guint egg_paned_get_n_children (EggPaned *self); -GtkWidget *egg_paned_get_nth_child (EggPaned *self, - guint nth); - -G_END_DECLS diff --git a/src/libsysprof-ui/egg-paned.c b/src/libsysprof-ui/egg-paned.c deleted file mode 100644 index 25b0d82c..00000000 --- a/src/libsysprof-ui/egg-paned.c +++ /dev/null @@ -1,593 +0,0 @@ -/* egg-paned.c - * - * Copyright 2021 Christian Hergert - * - * This file 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 3 of the License, or (at your option) - * any later version. - * - * This file 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 . - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ - -#include "config.h" - -#include - -#include "egg-paned-private.h" -#include "egg-resizer-private.h" - -struct _EggPaned -{ - GtkWidget parent_instance; - GtkOrientation orientation; -}; - -static void buildable_iface_init (GtkBuildableIface *iface); - -G_DEFINE_TYPE_WITH_CODE (EggPaned, egg_paned, GTK_TYPE_WIDGET, - G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, buildable_iface_init) - G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL)) - -enum { - PROP_0, - N_PROPS, - - PROP_ORIENTATION, -}; - -static void -update_orientation (GtkWidget *widget, - GtkOrientation orientation) -{ - if (orientation == GTK_ORIENTATION_HORIZONTAL) - { - gtk_widget_remove_css_class (widget, "vertical"); - gtk_widget_add_css_class (widget, "horizontal"); - } - else - { - gtk_widget_remove_css_class (widget, "horizontal"); - gtk_widget_add_css_class (widget, "vertical"); - } - - gtk_accessible_update_property (GTK_ACCESSIBLE (widget), - GTK_ACCESSIBLE_PROPERTY_ORIENTATION, orientation, - -1); -} - -/** - * egg_paned_new: - * - * Create a new #EggPaned. - * - * Returns: (transfer full): a newly created #EggPaned - */ -GtkWidget * -egg_paned_new (void) -{ - return g_object_new (EGG_TYPE_PANED, NULL); -} - -static void -egg_paned_set_orientation (EggPaned *self, - GtkOrientation orientation) -{ - GtkPositionType pos; - - g_assert (EGG_IS_PANED (self)); - g_assert (orientation == GTK_ORIENTATION_HORIZONTAL || - orientation == GTK_ORIENTATION_VERTICAL); - - if (self->orientation == orientation) - return; - - self->orientation = orientation; - - if (self->orientation == GTK_ORIENTATION_HORIZONTAL) - pos = GTK_POS_LEFT; - else - pos = GTK_POS_TOP; - - for (GtkWidget *child = gtk_widget_get_last_child (GTK_WIDGET (self)); - child != NULL; - child = gtk_widget_get_prev_sibling (child)) - { - g_assert (EGG_IS_RESIZER (child)); - - egg_resizer_set_position (EGG_RESIZER (child), pos); - } - - update_orientation (GTK_WIDGET (self), self->orientation); - gtk_widget_queue_resize (GTK_WIDGET (self)); - g_object_notify (G_OBJECT (self), "orientation"); -} - -static void -egg_paned_measure (GtkWidget *widget, - GtkOrientation orientation, - int for_size, - int *minimum, - int *natural, - int *minimum_baseline, - int *natural_baseline) -{ - EggPaned *self = (EggPaned *)widget; - - g_assert (EGG_IS_PANED (self)); - - *minimum = 0; - *natural = 0; - *minimum_baseline = -1; - *natural_baseline = -1; - - for (GtkWidget *child = gtk_widget_get_first_child (widget); - child != NULL; - child = gtk_widget_get_next_sibling (child)) - { - int child_min, child_nat; - - gtk_widget_measure (child, orientation, for_size, &child_min, &child_nat, NULL, NULL); - - if (orientation == self->orientation) - { - *minimum += child_min; - *natural += child_nat; - } - else - { - *minimum = MAX (*minimum, child_min); - *natural = MAX (*natural, child_nat); - } - } -} - -typedef struct -{ - GtkWidget *widget; - GtkRequisition min_request; - GtkRequisition nat_request; - GtkAllocation alloc; -} ChildAllocation; - -static void -egg_paned_size_allocate (GtkWidget *widget, - int width, - int height, - int baseline) -{ - EggPaned *self = (EggPaned *)widget; - ChildAllocation *allocs; - ChildAllocation *last_alloc = NULL; - GtkOrientation orientation; - guint n_children = 0; - guint n_expand = 0; - guint i; - int extra_width = width; - int extra_height = height; - int expand_width; - int expand_height; - int x, y; - - g_assert (EGG_IS_PANED (self)); - - GTK_WIDGET_CLASS (egg_paned_parent_class)->size_allocate (widget, width, height, baseline); - - n_children = egg_paned_get_n_children (self); - - if (n_children == 1) - { - GtkWidget *child = gtk_widget_get_first_child (widget); - GtkAllocation alloc = { 0, 0, width, height }; - - if (gtk_widget_get_visible (child)) - { - gtk_widget_size_allocate (child, &alloc, -1); - return; - } - } - - orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (self)); - allocs = g_newa (ChildAllocation, n_children); - memset (allocs, 0, sizeof *allocs * n_children); - - /* Give min size to each of the children */ - i = 0; - for (GtkWidget *child = gtk_widget_get_first_child (GTK_WIDGET (self)); - child != NULL; - child = gtk_widget_get_next_sibling (child), i++) - { - ChildAllocation *child_alloc = &allocs[i]; - - if (!gtk_widget_get_visible (child)) - continue; - - gtk_widget_measure (child, GTK_ORIENTATION_HORIZONTAL, height, - &child_alloc->min_request.width, - &child_alloc->nat_request.width, - NULL, NULL); - gtk_widget_measure (child, GTK_ORIENTATION_VERTICAL, width, - &child_alloc->min_request.height, - &child_alloc->nat_request.height, - NULL, NULL); - - child_alloc->alloc.width = child_alloc->min_request.width; - child_alloc->alloc.height = child_alloc->min_request.height; - - n_expand += gtk_widget_compute_expand (child, orientation); - - if (orientation == GTK_ORIENTATION_HORIZONTAL) - { - extra_width -= child_alloc->alloc.width; - child_alloc->alloc.height = height; - } - else - { - extra_height -= child_alloc->alloc.height; - child_alloc->alloc.width = width; - } - } - - /* Now try to distribute extra space for natural size */ - i = 0; - for (GtkWidget *child = gtk_widget_get_first_child (GTK_WIDGET (self)); - child != NULL; - child = gtk_widget_get_next_sibling (child), i++) - { - ChildAllocation *child_alloc = &allocs[i]; - - if (!gtk_widget_get_visible (child)) - continue; - - if (orientation == GTK_ORIENTATION_HORIZONTAL) - { - int taken = MIN (extra_width, child_alloc->nat_request.width - child_alloc->alloc.width); - - if (taken > 0) - { - child_alloc->alloc.width += taken; - extra_width -= taken; - } - } - else - { - int taken = MIN (extra_height, child_alloc->nat_request.height - child_alloc->alloc.height); - - if (taken > 0) - { - child_alloc->alloc.height += taken; - extra_height -= taken; - } - } - - last_alloc = child_alloc; - } - - /* Now give extra space for those that expand */ - expand_width = n_expand ? extra_width / n_expand : 0; - expand_height = n_expand ? extra_height / n_expand : 0; - i = n_children; - for (GtkWidget *child = gtk_widget_get_last_child (GTK_WIDGET (self)); - child != NULL; - child = gtk_widget_get_prev_sibling (child), i--) - { - ChildAllocation *child_alloc = &allocs[i-1]; - - if (!gtk_widget_get_visible (child)) - continue; - - if (!gtk_widget_compute_expand (child, orientation)) - continue; - - if (orientation == GTK_ORIENTATION_HORIZONTAL) - { - child_alloc->alloc.width += expand_width; - extra_width -= expand_width; - } - else - { - child_alloc->alloc.height += expand_height; - extra_height -= expand_height; - } - } - - /* Give any leftover to the last visible child */ - if (last_alloc) - { - if (orientation == GTK_ORIENTATION_HORIZONTAL) - last_alloc->alloc.width += extra_width; - else - last_alloc->alloc.height += extra_height; - } - - i = 0; - x = 0; - y = 0; - for (GtkWidget *child = gtk_widget_get_first_child (GTK_WIDGET (self)); - child != NULL; - child = gtk_widget_get_next_sibling (child), i++) - { - ChildAllocation *child_alloc = &allocs[i]; - - child_alloc->alloc.x = x; - child_alloc->alloc.y = y; - - if (orientation == GTK_ORIENTATION_HORIZONTAL) - x += child_alloc->alloc.width; - else - y += child_alloc->alloc.height; - - gtk_widget_size_allocate (child, &child_alloc->alloc, -1); - } -} - -static void -egg_paned_dispose (GObject *object) -{ - EggPaned *self = (EggPaned *)object; - GtkWidget *child; - - child = gtk_widget_get_first_child (GTK_WIDGET (self)); - while (child) - { - GtkWidget *next = gtk_widget_get_next_sibling (child); - gtk_widget_unparent (child); - child = next; - } - - G_OBJECT_CLASS (egg_paned_parent_class)->dispose (object); -} - -static void -egg_paned_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - EggPaned *self = EGG_PANED (object); - - switch (prop_id) - { - case PROP_ORIENTATION: - g_value_set_enum (value, self->orientation); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -egg_paned_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - EggPaned *self = EGG_PANED (object); - - switch (prop_id) - { - case PROP_ORIENTATION: - egg_paned_set_orientation (self, g_value_get_enum (value)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -egg_paned_class_init (EggPanedClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); - - object_class->dispose = egg_paned_dispose; - object_class->get_property = egg_paned_get_property; - object_class->set_property = egg_paned_set_property; - - widget_class->measure = egg_paned_measure; - widget_class->size_allocate = egg_paned_size_allocate; - - g_object_class_override_property (object_class, PROP_ORIENTATION, "orientation"); - - gtk_widget_class_set_css_name (widget_class, "eggpaned"); -} - -static void -egg_paned_init (EggPaned *self) -{ - self->orientation = GTK_ORIENTATION_HORIZONTAL; - - update_orientation (GTK_WIDGET (self), self->orientation); -} - -static void -egg_paned_update_handles (EggPaned *self) -{ - GtkWidget *child; - - g_assert (EGG_IS_PANED (self)); - - for (child = gtk_widget_get_first_child (GTK_WIDGET (self)); - child != NULL; - child = gtk_widget_get_next_sibling (child)) - { - GtkWidget *handle; - - g_assert (EGG_IS_RESIZER (child)); - - if ((handle = egg_resizer_get_handle (EGG_RESIZER (child)))) - gtk_widget_show (handle); - } - - if ((child = gtk_widget_get_last_child (GTK_WIDGET (self)))) - { - GtkWidget *handle = egg_resizer_get_handle (EGG_RESIZER (child)); - gtk_widget_hide (handle); - } -} - -void -egg_paned_remove (EggPaned *self, - GtkWidget *child) -{ - GtkWidget *resizer; - - g_return_if_fail (EGG_IS_PANED (self)); - g_return_if_fail (GTK_IS_WIDGET (child)); - - resizer = gtk_widget_get_ancestor (child, EGG_TYPE_RESIZER); - g_return_if_fail (resizer != NULL && - gtk_widget_get_parent (resizer) == GTK_WIDGET (self)); - gtk_widget_unparent (resizer); - egg_paned_update_handles (self); - gtk_widget_queue_resize (GTK_WIDGET (self)); -} - -void -egg_paned_insert (EggPaned *self, - int position, - GtkWidget *child) -{ - GtkPositionType pos; - GtkWidget *resizer; - - g_return_if_fail (EGG_IS_PANED (self)); - g_return_if_fail (GTK_IS_WIDGET (child)); - g_return_if_fail (gtk_widget_get_parent (child) == NULL); - - if (self->orientation == GTK_ORIENTATION_HORIZONTAL) - pos = GTK_POS_LEFT; - else - pos = GTK_POS_TOP; - - resizer = egg_resizer_new (pos); - egg_resizer_set_child (EGG_RESIZER (resizer), child); - - if (position < 0) - gtk_widget_insert_before (GTK_WIDGET (resizer), GTK_WIDGET (self), NULL); - else if (position == 0) - gtk_widget_insert_after (GTK_WIDGET (resizer), GTK_WIDGET (self), NULL); - else - { - GtkWidget *sibling = gtk_widget_get_first_child (GTK_WIDGET (self)); - - for (int i = position; i > 0 && sibling != NULL; i--) - sibling = gtk_widget_get_next_sibling (sibling); - - gtk_widget_insert_before (GTK_WIDGET (resizer), GTK_WIDGET (self), sibling); - } - - egg_paned_update_handles (self); - - gtk_widget_queue_resize (GTK_WIDGET (self)); -} - -void -egg_paned_append (EggPaned *self, - GtkWidget *child) -{ - egg_paned_insert (self, -1, child); -} - -void -egg_paned_prepend (EggPaned *self, - GtkWidget *child) -{ - egg_paned_insert (self, 0, child); -} - -void -egg_paned_insert_after (EggPaned *self, - GtkWidget *child, - GtkWidget *sibling) -{ - int position = 0; - - g_return_if_fail (EGG_IS_PANED (self)); - g_return_if_fail (GTK_IS_WIDGET (child)); - g_return_if_fail (!sibling || GTK_IS_WIDGET (sibling)); - - if (sibling == NULL) - { - egg_paned_prepend (self, child); - return; - } - - /* TODO: We should reverse insert() to call this */ - - for (GtkWidget *ancestor = gtk_widget_get_first_child (GTK_WIDGET (self)); - ancestor != NULL; - ancestor = gtk_widget_get_next_sibling (ancestor)) - { - position++; - - if (sibling == ancestor || gtk_widget_is_ancestor (sibling, ancestor)) - break; - } - - egg_paned_insert (self, position, child); -} - -guint -egg_paned_get_n_children (EggPaned *self) -{ - guint count = 0; - - for (GtkWidget *child = gtk_widget_get_first_child (GTK_WIDGET (self)); - child != NULL; - child = gtk_widget_get_next_sibling (child)) - count++; - - return count; -} - -GtkWidget * -egg_paned_get_nth_child (EggPaned *self, - guint nth) -{ - g_return_val_if_fail (EGG_IS_PANED (self), NULL); - - for (GtkWidget *child = gtk_widget_get_first_child (GTK_WIDGET (self)); - child != NULL; - child = gtk_widget_get_next_sibling (child)) - { - g_assert (EGG_IS_RESIZER (child)); - - if (nth == 0) - return egg_resizer_get_child (EGG_RESIZER (child)); - - nth--; - } - - return NULL; -} - -static void -egg_paned_add_child (GtkBuildable *buildable, - GtkBuilder *builder, - GObject *child, - const char *type) -{ - if (GTK_IS_WIDGET (child)) - egg_paned_append (EGG_PANED (buildable), GTK_WIDGET (child)); - else - g_warning ("Cannot add child of type %s to %s", - G_OBJECT_TYPE_NAME (child), - G_OBJECT_TYPE_NAME (buildable)); -} - -static void -buildable_iface_init (GtkBuildableIface *iface) -{ - iface->add_child = egg_paned_add_child; -} diff --git a/src/libsysprof-ui/egg-resizer-private.h b/src/libsysprof-ui/egg-resizer-private.h deleted file mode 100644 index 58db56c3..00000000 --- a/src/libsysprof-ui/egg-resizer-private.h +++ /dev/null @@ -1,40 +0,0 @@ -/* egg-resizer.h - * - * Copyright 2021 Christian Hergert - * - * This file 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 3 of the License, or (at your option) - * any later version. - * - * This file 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 . - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ - -#pragma once - -#include - -G_BEGIN_DECLS - -#define EGG_TYPE_RESIZER (egg_resizer_get_type()) - -G_DECLARE_FINAL_TYPE (EggResizer, egg_resizer, EGG, RESIZER, GtkWidget) - -GtkWidget *egg_resizer_new (GtkPositionType position); -GtkPositionType egg_resizer_get_position (EggResizer *self); -void egg_resizer_set_position (EggResizer *self, - GtkPositionType position); -GtkWidget *egg_resizer_get_child (EggResizer *self); -void egg_resizer_set_child (EggResizer *self, - GtkWidget *child); -GtkWidget *egg_resizer_get_handle (EggResizer *self); - -G_END_DECLS diff --git a/src/libsysprof-ui/egg-resizer.c b/src/libsysprof-ui/egg-resizer.c deleted file mode 100644 index c2b427bc..00000000 --- a/src/libsysprof-ui/egg-resizer.c +++ /dev/null @@ -1,495 +0,0 @@ -/* egg-resizer.c - * - * Copyright 2021 Christian Hergert - * - * This file 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 3 of the License, or (at your option) - * any later version. - * - * This file 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 . - * - * SPDX-License-Identifier: LGPL-3.0-or-later - */ - -#include "config.h" - -#include "egg-handle-private.h" -#include "egg-resizer-private.h" - -#define HANDLE_SIZE 8 - -struct _EggResizer -{ - GtkWidget parent_instance; - - EggHandle *handle; - GtkWidget *child; - - double drag_orig_size; - double drag_position; - - GtkPositionType position : 3; -}; - -G_DEFINE_TYPE (EggResizer, egg_resizer, GTK_TYPE_WIDGET) - -enum { - PROP_0, - PROP_CHILD, - N_PROPS -}; - -static GParamSpec *properties [N_PROPS]; - -static void -egg_resizer_drag_begin_cb (EggResizer *self, - double start_x, - double start_y, - GtkGestureDrag *drag) -{ - GtkAllocation child_alloc; - GtkAllocation handle_alloc; - - g_assert (EGG_IS_RESIZER (self)); - g_assert (GTK_IS_GESTURE_DRAG (drag)); - - if (self->child == NULL) - return; - - switch (self->position) - { - case GTK_POS_LEFT: - if (start_x > gtk_widget_get_width (GTK_WIDGET (self)) - HANDLE_SIZE) - goto start_drag; - break; - - case GTK_POS_RIGHT: - if (start_x <= HANDLE_SIZE) - goto start_drag; - break; - - case GTK_POS_TOP: - if (start_y > gtk_widget_get_height (GTK_WIDGET (self)) - HANDLE_SIZE) - goto start_drag; - break; - - case GTK_POS_BOTTOM: - if (start_y <= HANDLE_SIZE) - goto start_drag; - break; - - default: - g_assert_not_reached (); - break; - } - - gtk_gesture_set_state (GTK_GESTURE (drag), - GTK_EVENT_SEQUENCE_DENIED); - - return; - -start_drag: - - gtk_widget_get_allocation (self->child, &child_alloc); - gtk_widget_get_allocation (GTK_WIDGET (self->handle), &handle_alloc); - - if (self->position == GTK_POS_LEFT || - self->position == GTK_POS_RIGHT) - { - self->drag_orig_size = child_alloc.width + handle_alloc.width; - gtk_widget_set_hexpand (self->child, FALSE); - } - else - { - self->drag_orig_size = child_alloc.height + handle_alloc.height; - gtk_widget_set_vexpand (self->child, FALSE); - } - - self->drag_position = self->drag_orig_size; - - gtk_widget_queue_resize (GTK_WIDGET (self)); -} - -static void -egg_resizer_drag_update_cb (EggResizer *self, - double offset_x, - double offset_y, - GtkGestureDrag *drag) -{ - g_assert (EGG_IS_RESIZER (self)); - g_assert (GTK_IS_GESTURE_DRAG (drag)); - - if (self->position == GTK_POS_LEFT) - self->drag_position = self->drag_orig_size + offset_x; - else if (self->position == GTK_POS_RIGHT) - self->drag_position = gtk_widget_get_width (GTK_WIDGET (self)) - offset_x; - else if (self->position == GTK_POS_TOP) - self->drag_position = self->drag_orig_size + offset_y; - else if (self->position == GTK_POS_BOTTOM) - self->drag_position = gtk_widget_get_height (GTK_WIDGET (self)) - offset_y; - - gtk_widget_queue_resize (GTK_WIDGET (self)); -} - -static void -egg_resizer_drag_end_cb (EggResizer *self, - double offset_x, - double offset_y, - GtkGestureDrag *drag) -{ - g_assert (EGG_IS_RESIZER (self)); - g_assert (GTK_IS_GESTURE_DRAG (drag)); -} - -GtkWidget * -egg_resizer_new (GtkPositionType position) -{ - EggResizer *self; - - self = g_object_new (EGG_TYPE_RESIZER, NULL); - self->position = position; - self->handle = EGG_HANDLE (egg_handle_new (position)); - gtk_widget_set_parent (GTK_WIDGET (self->handle), GTK_WIDGET (self)); - - return GTK_WIDGET (self); -} - -static void -egg_resizer_measure (GtkWidget *widget, - GtkOrientation orientation, - int for_size, - int *minimum, - int *natural, - int *minimum_baseline, - int *natural_baseline) -{ - EggResizer *self = (EggResizer *)widget; - - g_assert (EGG_IS_RESIZER (self)); - - *minimum = 0; - *natural = 0; - *minimum_baseline = -1; - *natural_baseline = -1; - - if (self->child != NULL) - gtk_widget_measure (self->child, - orientation, - for_size, - minimum, natural, - NULL, NULL); - - if ((orientation == GTK_ORIENTATION_HORIZONTAL && - (self->position == GTK_POS_LEFT || - self->position == GTK_POS_RIGHT)) || - (orientation == GTK_ORIENTATION_VERTICAL && - (self->position == GTK_POS_TOP || - self->position == GTK_POS_BOTTOM))) - { - int handle_min, handle_nat; - - if (self->drag_position != 0) - { - if (self->drag_position > *minimum) - *natural = self->drag_position; - else if (self->drag_position < *minimum) - *natural = *minimum; - } - - if (gtk_widget_get_visible (GTK_WIDGET (self->handle))) - { - gtk_widget_measure (GTK_WIDGET (self->handle), - orientation, for_size, - &handle_min, &handle_nat, - NULL, NULL); - - *minimum += handle_min; - *natural += handle_nat; - } - } -} - -static void -egg_resizer_size_allocate (GtkWidget *widget, - int width, - int height, - int baseline) -{ - EggResizer *self = (EggResizer *)widget; - GtkOrientation orientation; - GtkAllocation child_alloc; - GtkAllocation handle_alloc; - int handle_min = 0, handle_nat = 0; - - g_assert (EGG_IS_RESIZER (self)); - - if (self->position == GTK_POS_LEFT || - self->position == GTK_POS_RIGHT) - orientation = GTK_ORIENTATION_HORIZONTAL; - else - orientation = GTK_ORIENTATION_VERTICAL; - - if (gtk_widget_get_visible (GTK_WIDGET (self->handle))) - gtk_widget_measure (GTK_WIDGET (self->handle), - orientation, - -1, - &handle_min, &handle_nat, - NULL, NULL); - - switch (self->position) - { - case GTK_POS_LEFT: - handle_alloc.x = width - handle_min; - handle_alloc.width = handle_min; - handle_alloc.y = 0; - handle_alloc.height = height; - child_alloc.x = 0; - child_alloc.y = 0; - child_alloc.width = width - handle_min; - child_alloc.height = height; - break; - - case GTK_POS_RIGHT: - handle_alloc.x = 0; - handle_alloc.width = handle_min; - handle_alloc.y = 0; - handle_alloc.height = height; - child_alloc.x = handle_min; - child_alloc.y = 0; - child_alloc.width = width - handle_min; - child_alloc.height = height; - break; - - case GTK_POS_TOP: - handle_alloc.x = 0; - handle_alloc.width = width; - handle_alloc.y = height - handle_min; - handle_alloc.height = handle_min; - child_alloc.x = 0; - child_alloc.y = 0; - child_alloc.width = width; - child_alloc.height = height - handle_min; - break; - - case GTK_POS_BOTTOM: - handle_alloc.x = 0; - handle_alloc.width = width; - handle_alloc.y = 0; - handle_alloc.height = handle_min; - child_alloc.x = 0; - child_alloc.y = handle_min; - child_alloc.width = width; - child_alloc.height = height - handle_min; - break; - - default: - g_assert_not_reached (); - } - - if (gtk_widget_get_mapped (GTK_WIDGET (self->handle))) - gtk_widget_size_allocate (GTK_WIDGET (self->handle), &handle_alloc, -1); - - if (self->child != NULL && - gtk_widget_get_mapped (self->child)) - gtk_widget_size_allocate (self->child, &child_alloc, -1); -} - -static void -egg_resizer_compute_expand (GtkWidget *widget, - gboolean *hexpand, - gboolean *vexpand) -{ - EggResizer *self = EGG_RESIZER (widget); - - if (self->child != NULL) - { - *hexpand = gtk_widget_compute_expand (self->child, GTK_ORIENTATION_HORIZONTAL); - *vexpand = gtk_widget_compute_expand (self->child, GTK_ORIENTATION_VERTICAL); - } - else - { - *hexpand = FALSE; - *vexpand = FALSE; - } -} - -static void -egg_resizer_dispose (GObject *object) -{ - EggResizer *self = (EggResizer *)object; - - if (self->handle) - gtk_widget_unparent (GTK_WIDGET (self->handle)); - self->handle = NULL; - - if (self->child) - gtk_widget_unparent (self->child); - self->child = NULL; - - G_OBJECT_CLASS (egg_resizer_parent_class)->dispose (object); -} - -static void -egg_resizer_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - EggResizer *self = EGG_RESIZER (object); - - switch (prop_id) - { - case PROP_CHILD: - g_value_set_object (value, egg_resizer_get_child (self)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -egg_resizer_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - EggResizer *self = EGG_RESIZER (object); - - switch (prop_id) - { - case PROP_CHILD: - egg_resizer_set_child (self, g_value_get_object (value)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -egg_resizer_class_init (EggResizerClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); - - object_class->dispose = egg_resizer_dispose; - object_class->get_property = egg_resizer_get_property; - object_class->set_property = egg_resizer_set_property; - - widget_class->compute_expand = egg_resizer_compute_expand; - widget_class->measure = egg_resizer_measure; - widget_class->size_allocate = egg_resizer_size_allocate; - - properties [PROP_CHILD] = - g_param_spec_object ("child", - "Child", - "Child", - GTK_TYPE_WIDGET, - (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_css_name (widget_class, "eggresizer"); -} - -static void -egg_resizer_init (EggResizer *self) -{ - GtkGesture *gesture; - - gesture = gtk_gesture_drag_new (); - gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (gesture), GTK_PHASE_CAPTURE); - g_signal_connect_object (gesture, - "drag-begin", - G_CALLBACK (egg_resizer_drag_begin_cb), - self, - G_CONNECT_SWAPPED); - g_signal_connect_object (gesture, - "drag-update", - G_CALLBACK (egg_resizer_drag_update_cb), - self, - G_CONNECT_SWAPPED); - g_signal_connect_object (gesture, - "drag-end", - G_CALLBACK (egg_resizer_drag_end_cb), - self, - G_CONNECT_SWAPPED); - gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (gesture)); -} - -/** - * egg_resizer_get_child: - * @self: a #EggResizer - * - * Gets the child widget of the resizer. - * - * Returns: (transfer none) (nullable): A #GtkWidget or %NULL - */ -GtkWidget * -egg_resizer_get_child (EggResizer *self) -{ - g_return_val_if_fail (EGG_IS_RESIZER (self), NULL); - - return self->child; -} - -void -egg_resizer_set_child (EggResizer *self, - GtkWidget *child) -{ - g_return_if_fail (EGG_IS_RESIZER (self)); - g_return_if_fail (!child || GTK_IS_WIDGET (child)); - - if (child == self->child) - return; - - g_clear_pointer (&self->child, gtk_widget_unparent); - - self->child = child; - - if (self->child != NULL) - gtk_widget_insert_before (self->child, - GTK_WIDGET (self), - GTK_WIDGET (self->handle)); - - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CHILD]); -} - -GtkPositionType -egg_resizer_get_position (EggResizer *self) -{ - g_return_val_if_fail (EGG_IS_RESIZER (self), 0); - - return self->position; -} - -void -egg_resizer_set_position (EggResizer *self, - GtkPositionType position) -{ - g_return_if_fail (EGG_IS_RESIZER (self)); - - if (position != self->position) - { - self->position = position; - - egg_handle_set_position (self->handle, position); - gtk_widget_queue_resize (GTK_WIDGET (self)); - } -} - -GtkWidget * -egg_resizer_get_handle (EggResizer *self) -{ - g_return_val_if_fail (EGG_IS_RESIZER (self), NULL); - - return GTK_WIDGET (self->handle); -} diff --git a/src/libsysprof-ui/libsysprof-ui.gresource.xml b/src/libsysprof-ui/libsysprof-ui.gresource.xml deleted file mode 100644 index e5aa0593..00000000 --- a/src/libsysprof-ui/libsysprof-ui.gresource.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - css/SysprofEnvironEditor-shared.css - css/SysprofDisplay-shared.css - css/SysprofProfilerAssistant-shared.css - - - ../../data/icons/org.gnome.Sysprof.svg - ../../data/icons/symbolic/apps/org.gnome.Sysprof-symbolic.svg - ../../data/icons/symbolic/apps/org.gnome.Sysprof-symbolic.svg - - - - sysprof-aid-icon.ui - sysprof-callgraph-page.ui - sysprof-details-page.ui - sysprof-display.ui - sysprof-environ-editor-row.ui - sysprof-failed-state-view.ui - sysprof-logs-page.ui - sysprof-marks-page.ui - sysprof-memprof-page.ui - sysprof-process-model-row.ui - sysprof-profiler-assistant.ui - sysprof-recording-state-view.ui - sysprof-tab.ui - sysprof-visualizers-frame.ui - - diff --git a/src/libsysprof-ui/meson.build b/src/libsysprof-ui/meson.build deleted file mode 100644 index 0017eedb..00000000 --- a/src/libsysprof-ui/meson.build +++ /dev/null @@ -1,136 +0,0 @@ -libsysprof_ui_public_sources = [ - 'sysprof-check.c', - 'sysprof-display.c', - 'sysprof-model-filter.c', - 'sysprof-notebook.c', - 'sysprof-page.c', - 'sysprof-process-model-row.c', - 'sysprof-visualizer.c', - 'sysprof-visualizer-group.c', - 'sysprof-zoom-manager.c', -] - -libsysprof_ui_private_sources = [ - 'egg-handle.c', - 'egg-paned.c', - 'egg-resizer.c', - - 'pointcache.c', - 'rectangles.c', - 'sysprof-aid.c', - 'sysprof-aid-icon.c', - 'sysprof-battery-aid.c', - 'sysprof-cairo.c', - 'sysprof-callgraph-aid.c', - 'sysprof-callgraph-page.c', - 'sysprof-cell-renderer-duration.c', - 'sysprof-cell-renderer-percent.c', - 'sysprof-cell-renderer-progress.c', - 'sysprof-color-cycle.c', - 'sysprof-counters-aid.c', - 'sysprof-cpu-aid.c', - 'sysprof-depth-visualizer.c', - 'sysprof-details-page.c', - 'sysprof-diskstat-aid.c', - 'sysprof-display.c', - 'sysprof-duplex-visualizer.c', - 'sysprof-environ.c', - 'sysprof-environ-editor.c', - 'sysprof-environ-editor-row.c', - 'sysprof-environ-variable.c', - 'sysprof-failed-state-view.c', - 'sysprof-line-visualizer.c', - 'sysprof-log-model.c', - 'sysprof-logs-aid.c', - 'sysprof-logs-page.c', - 'sysprof-mark-detail.c', - 'sysprof-marks-aid.c', - 'sysprof-marks-model.c', - 'sysprof-marks-page.c', - 'sysprof-mark-visualizer.c', - 'sysprof-memory-aid.c', - 'sysprof-memprof-aid.c', - 'sysprof-memprof-page.c', - 'sysprof-memprof-visualizer.c', - 'sysprof-netdev-aid.c', - 'sysprof-procs-visualizer.c', - 'sysprof-profiler-assistant.c', - 'sysprof-proxy-aid.c', - 'sysprof-rapl-aid.c', - 'sysprof-recording-state-view.c', - 'sysprof-scrollmap.c', - 'sysprof-tab.c', - 'sysprof-theme-manager.c', - 'sysprof-time-label.c', - 'sysprof-time-visualizer.c', - 'sysprof-visualizer-group-header.c', - 'sysprof-visualizers-frame.c', - 'sysprof-visualizer-ticks.c', - '../stackstash.c', -] - -libsysprof_ui_public_headers = [ - 'sysprof-check.h', - 'sysprof-display.h', - 'sysprof-model-filter.h', - 'sysprof-notebook.h', - 'sysprof-page.h', - 'sysprof-process-model-row.h', - 'sysprof-visualizer.h', - 'sysprof-visualizer-group.h', - 'sysprof-zoom-manager.h', - 'sysprof-ui.h', -] - -libsysprof_ui_resources = gnome.compile_resources( - 'libsysprof-ui-resources', - 'libsysprof-ui.gresource.xml', - c_name: 'lisysprof_ui', -) - -# Subset of dependencies used in generating the pkg-config file -libsysprof_ui_pkg_deps = [ - dependency('gio-2.0', version: glib_req_version), - dependency('gtk4', version: gtk_req_version), - dependency('libadwaita-1'), -] - -libsysprof_ui_deps = libsysprof_ui_pkg_deps + [ - libsysprof_dep, -] - -# Meson's pkgconfig module wants to see a library here, not an internal -# dependency object -libsysprof_ui_pkg_deps += libsysprof - -libsysprof_ui = shared_library( - 'sysprof-ui-@0@'.format(libsysprof_ui_api_version), - libsysprof_ui_public_sources + libsysprof_ui_private_sources + libsysprof_ui_resources, - - dependencies: libsysprof_ui_deps + [librax_dep], - install_dir: get_option('libdir'), - install: true, - c_args: [ '-DSYSPROF_UI_COMPILATION' ], - gnu_symbol_visibility: 'hidden', -) - -libsysprof_ui_dep = declare_dependency( - link_with: libsysprof_ui, - dependencies: libsysprof_ui_deps, - include_directories: include_directories('.'), -) -meson.override_dependency('sysprof-ui-@0@'.format(libsysprof_api_version), libsysprof_ui_dep) - -pkgconfig.generate( - libsysprof_ui, - subdirs: [ sysprof_ui_header_subdir ], - description: 'The UI library for GTK applications embedding sysprof', - install_dir: join_paths(get_option('libdir'), 'pkgconfig'), - requires: [ 'gio-2.0', 'gtk4' ], - libraries_private: libsysprof_ui_pkg_deps, - variables: [ - 'datadir=' + datadir_for_pc_file, - ], -) - -install_headers(libsysprof_ui_public_headers, subdir: sysprof_ui_header_subdir) diff --git a/src/libsysprof-ui/pointcache.c b/src/libsysprof-ui/pointcache.c deleted file mode 100644 index c16304ff..00000000 --- a/src/libsysprof-ui/pointcache.c +++ /dev/null @@ -1,116 +0,0 @@ -/* pointcache.c - * - * Copyright 2016-2019 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 - */ - -#define G_LOG_DOMAIN "pointcache" - -#include "pointcache.h" - -struct _PointCache -{ - volatile gint ref_count; - GHashTable *sets; -}; - -static void -point_cache_finalize (PointCache *self) -{ - g_clear_pointer (&self->sets, g_hash_table_unref); - g_slice_free (PointCache, self); -} - -PointCache * -point_cache_ref (PointCache *self) -{ - g_return_val_if_fail (self != NULL, NULL); - g_return_val_if_fail (self->ref_count > 0, NULL); - - g_atomic_int_inc (&self->ref_count); - - return self; -} - -void -point_cache_unref (PointCache *self) -{ - g_return_if_fail (self != NULL); - g_return_if_fail (self->ref_count > 0); - - if (g_atomic_int_dec_and_test (&self->ref_count)) - point_cache_finalize (self); -} - -PointCache * -point_cache_new (void) -{ - PointCache *self; - - self = g_slice_new0 (PointCache); - self->ref_count = 1; - self->sets = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_array_unref); - - return self; -} - -void -point_cache_add_set (PointCache *self, - guint set_id) -{ - g_hash_table_insert (self->sets, - GUINT_TO_POINTER (set_id), - g_array_new (FALSE, FALSE, sizeof (Point))); -} - -gboolean -point_cache_contains_set (PointCache *self, - guint set_id) -{ - return g_hash_table_contains (self->sets, GUINT_TO_POINTER (set_id)); -} - -void -point_cache_add_point_to_set (PointCache *self, - guint set_id, - gdouble x, - gdouble y) -{ - GArray *ar; - Point point = { x, y }; - - ar = g_hash_table_lookup (self->sets, GUINT_TO_POINTER (set_id)); - g_array_append_val (ar, point); -} - -const Point * -point_cache_get_points (PointCache *self, - guint set_id, - guint *n_points) -{ - GArray *ar; - - *n_points = 0; - - if ((ar = g_hash_table_lookup (self->sets, GUINT_TO_POINTER (set_id)))) - { - *n_points = ar->len; - return &g_array_index (ar, const Point, 0); - } - - return NULL; -} diff --git a/src/libsysprof-ui/pointcache.h b/src/libsysprof-ui/pointcache.h deleted file mode 100644 index 3aed8179..00000000 --- a/src/libsysprof-ui/pointcache.h +++ /dev/null @@ -1,52 +0,0 @@ -/* pointcache.h - * - * Copyright 2016-2019 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 - -typedef struct _PointCache PointCache; - -typedef struct -{ - gdouble x; - gdouble y; -} Point; - -PointCache *point_cache_new (void); -PointCache *point_cache_ref (PointCache *self); -void point_cache_unref (PointCache *self); -void point_cache_add_set (PointCache *self, - guint set_id); -gboolean point_cache_contains_set (PointCache *self, - guint set_id); -void point_cache_add_point_to_set (PointCache *self, - guint set_id, - gdouble x, - gdouble y); -const Point *point_cache_get_points (PointCache *self, - guint set_id, - guint *n_points); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC (PointCache, point_cache_unref) - -G_END_DECLS diff --git a/src/libsysprof-ui/rectangles.c b/src/libsysprof-ui/rectangles.c deleted file mode 100644 index 9db41038..00000000 --- a/src/libsysprof-ui/rectangles.c +++ /dev/null @@ -1,248 +0,0 @@ -/* rectangles.c - * - * Copyright 2018-2019 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 "rectangles.h" -#include "sysprof-color-cycle.h" -#include "sysprof-visualizer.h" - -typedef struct -{ - const gchar *name; - const gchar *message; - gint64 begin; - gint64 end; - GdkRectangle area; -} Rectangle; - -struct _Rectangles -{ - GStringChunk *strings; - GArray *rectangles; - GHashTable *y_indexes; - GHashTable *colors; - SysprofColorCycle *cycle; - gint64 begin_time; - gint64 end_time; - guint sorted : 1; -}; - -Rectangles * -rectangles_new (gint64 begin_time, - gint64 end_time) -{ - Rectangles *self; - - self = g_slice_new0 (Rectangles); - self->strings = g_string_chunk_new (4096); - self->rectangles = g_array_new (FALSE, FALSE, sizeof (Rectangle)); - self->y_indexes = g_hash_table_new (g_str_hash, g_str_equal); - self->colors = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_free); - self->cycle = sysprof_color_cycle_new (); - self->begin_time = begin_time; - self->end_time = end_time; - self->sorted = FALSE; - - return self; -} - -void -rectangles_add (Rectangles *self, - gint64 begin_time, - gint64 end_time, - const gchar *name, - const gchar *message) -{ - Rectangle rect = {0}; - - g_assert (self != NULL); - - if (message != NULL) - rect.message = g_string_chunk_insert_const (self->strings, message); - - if (name != NULL) - rect.name = g_string_chunk_insert_const (self->strings, name); - - rect.begin = begin_time; - rect.end = end_time; - - if (rect.end == rect.begin) - rect.area.width = 1; - - g_array_append_val (self->rectangles, rect); - - self->sorted = FALSE; -} - -void -rectangles_free (Rectangles *self) -{ - g_string_chunk_free (self->strings); - g_array_unref (self->rectangles); - g_hash_table_unref (self->colors); - g_hash_table_unref (self->y_indexes); - sysprof_color_cycle_unref (self->cycle); - g_slice_free (Rectangles, self); -} - -static gint -sort_rectangles (gconstpointer a, - gconstpointer b) -{ - const Rectangle *r1 = a; - const Rectangle *r2 = b; - gint64 r = r1->begin - r2->begin; - if (r == 0) - r = r1->end - r2->end; - if (r > 0) return 1; - else if (r < 0) return -1; - else return 0; -} - -static void -rectangles_sort (Rectangles *self) -{ - guint sequence = 0; - - g_assert (self != NULL); - - if (self->sorted) - return; - - g_array_sort (self->rectangles, sort_rectangles); - - g_hash_table_remove_all (self->y_indexes); - - for (guint i = 0; i < self->rectangles->len; i++) - { - const Rectangle *rect = &g_array_index (self->rectangles, Rectangle, i); - - if (!g_hash_table_contains (self->y_indexes, rect->name)) - { - GdkRGBA rgba; - - sysprof_color_cycle_next (self->cycle, &rgba); - g_hash_table_insert (self->y_indexes, (gchar *)rect->name, GUINT_TO_POINTER (++sequence)); - g_hash_table_insert (self->colors, (gchar *)rect->name, g_memdup2 (&rgba, sizeof rgba)); - } - } - - self->sorted = TRUE; -} - -void -rectangles_draw (Rectangles *self, - GtkWidget *row, - cairo_t *cr) -{ - GtkAllocation alloc; - gdouble range; - guint ns; - - g_assert (self != NULL); - g_assert (SYSPROF_IS_VISUALIZER (row)); - g_assert (cr != NULL); - - if (!self->sorted) - rectangles_sort (self); - - gtk_widget_get_allocation (row, &alloc); - ns = g_hash_table_size (self->y_indexes); - if (ns == 0 || alloc.height == 0) - return; - - range = self->end_time - self->begin_time; - - for (guint i = 0; i < self->rectangles->len; i++) - { - Rectangle *rect = &g_array_index (self->rectangles, Rectangle, i); - guint y_index = GPOINTER_TO_UINT (g_hash_table_lookup (self->y_indexes, rect->name)); - SysprofVisualizerRelativePoint in_points[2]; - SysprofVisualizerAbsolutePoint out_points[2]; - GdkRectangle r; - GdkRGBA *rgba; - - g_assert (y_index > 0); - g_assert (y_index <= ns); - - in_points[0].x = (rect->begin - self->begin_time) / range; - in_points[0].y = (y_index - 1) / (gdouble)ns; - in_points[1].x = (rect->end - self->begin_time) / range; - in_points[1].y = 0; - - sysprof_visualizer_translate_points (SYSPROF_VISUALIZER (row), - in_points, G_N_ELEMENTS (in_points), - out_points, G_N_ELEMENTS (out_points)); - - r.height = alloc.height / (gdouble)ns; - r.x = out_points[0].x; - r.y = out_points[0].y - r.height; - - if (rect->end <= rect->begin) - r.width = 1; - else - r.width = MAX (1, out_points[1].x - out_points[0].x); - - rect->area = r; - - rgba = g_hash_table_lookup (self->colors, rect->name); - - gdk_cairo_rectangle (cr, &r); - gdk_cairo_set_source_rgba (cr, rgba); - cairo_fill (cr); - } -} - -void -rectangles_set_end_time (Rectangles *self, - gint64 end_time) -{ - /* We might not know the real end time until we've exhausted the stream */ - self->end_time = end_time; -} - -gboolean -rectangles_query_tooltip (Rectangles *self, - GtkTooltip *tooltip, - const gchar *group, - gint x, - gint y) -{ - g_assert (self != NULL); - g_assert (GTK_IS_TOOLTIP (tooltip)); - - /* TODO: Sort, binary search, etc. */ - - for (guint i = 0; i < self->rectangles->len; i++) - { - const Rectangle *r = &g_array_index (self->rectangles, Rectangle, i); - - if (r->area.x <= x && - r->area.y <= y && - r->area.x + r->area.width >= x && - r->area.y + r->area.height >= y) - { - g_autofree gchar *text = g_strdup_printf ("%s:%s: %s", group, r->name, r->message); - gtk_tooltip_set_text (tooltip, text); - return TRUE; - } - } - - return FALSE; -} diff --git a/src/libsysprof-ui/rectangles.h b/src/libsysprof-ui/rectangles.h deleted file mode 100644 index f71c3a43..00000000 --- a/src/libsysprof-ui/rectangles.h +++ /dev/null @@ -1,48 +0,0 @@ -/* rectangles.h - * - * Copyright 2018-2019 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 - -typedef struct _Rectangles Rectangles; - -Rectangles *rectangles_new (gint64 begin_time, - gint64 end_time); -void rectangles_free (Rectangles *self); -void rectangles_draw (Rectangles *self, - GtkWidget *widget, - cairo_t *cr); -void rectangles_add (Rectangles *self, - gint64 begin_time, - gint64 end_time, - const gchar *name, - const gchar *message); -void rectangles_set_end_time (Rectangles *self, - gint64 end_time); -gboolean rectangles_query_tooltip (Rectangles *self, - GtkTooltip *tooltip, - const gchar *group, - gint x, - gint y); - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-aid-icon.c b/src/libsysprof-ui/sysprof-aid-icon.c deleted file mode 100644 index eba2b4ff..00000000 --- a/src/libsysprof-ui/sysprof-aid-icon.c +++ /dev/null @@ -1,211 +0,0 @@ -/* sysprof-aid-icon.c - * - * Copyright 2019 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 - */ - -#define G_LOG_DOMAIN "sysprof-aid-icon" - -#include "config.h" - -#include "sysprof-aid-icon.h" - -struct _SysprofAidIcon -{ - GtkFlowBoxChild parent_instance; - - SysprofAid *aid; - - /* Template Objects */ - GtkLabel *label; - GtkImage *image; - GtkImage *check; -}; - -G_DEFINE_TYPE (SysprofAidIcon, sysprof_aid_icon, GTK_TYPE_FLOW_BOX_CHILD) - -enum { - PROP_0, - PROP_AID, - PROP_SELECTED, - N_PROPS -}; - -static GParamSpec *properties [N_PROPS]; - -/** - * sysprof_aid_icon_new: - * - * Create a new #SysprofAidIcon. - * - * Returns: (transfer full): a newly created #SysprofAidIcon - */ -GtkWidget * -sysprof_aid_icon_new (SysprofAid *aid) -{ - g_return_val_if_fail (SYSPROF_IS_AID (aid), NULL); - - return g_object_new (SYSPROF_TYPE_AID_ICON, - "aid", aid, - NULL); -} - -gboolean -sysprof_aid_icon_is_selected (SysprofAidIcon *self) -{ - g_return_val_if_fail (SYSPROF_IS_AID_ICON (self), FALSE); - - return gtk_widget_get_visible (GTK_WIDGET (self->check)); -} - -/** - * sysprof_aid_icon_get_aid: - * - * Get the aid that is represented by the icon. - * - * Returns: (transfer none): a #SysprofAid - * - * Since: 3.34 - */ -SysprofAid * -sysprof_aid_icon_get_aid (SysprofAidIcon *self) -{ - g_return_val_if_fail (SYSPROF_IS_AID_ICON (self), NULL); - - return self->aid; -} - -static void -sysprof_aid_icon_set_aid (SysprofAidIcon *self, - SysprofAid *aid) -{ - g_return_if_fail (SYSPROF_IS_AID_ICON (self)); - g_return_if_fail (SYSPROF_IS_AID (aid)); - - if (g_set_object (&self->aid, aid)) - { - GIcon *icon = sysprof_aid_get_icon (aid); - const gchar *title = sysprof_aid_get_display_name (aid); - - g_object_set (self->image, "gicon", icon, NULL); - gtk_label_set_label (self->label, title); - } -} - -static void -sysprof_aid_icon_finalize (GObject *object) -{ - SysprofAidIcon *self = (SysprofAidIcon *)object; - - g_clear_object (&self->aid); - - G_OBJECT_CLASS (sysprof_aid_icon_parent_class)->finalize (object); -} - -static void -sysprof_aid_icon_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - SysprofAidIcon *self = SYSPROF_AID_ICON (object); - - switch (prop_id) - { - case PROP_AID: - g_value_set_object (value, sysprof_aid_icon_get_aid (self)); - break; - - case PROP_SELECTED: - g_value_set_boolean (value, gtk_widget_get_visible (GTK_WIDGET (self->check))); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_aid_icon_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - SysprofAidIcon *self = SYSPROF_AID_ICON (object); - - switch (prop_id) - { - case PROP_AID: - sysprof_aid_icon_set_aid (self, g_value_get_object (value)); - break; - - case PROP_SELECTED: - gtk_widget_set_visible (GTK_WIDGET (self->check), g_value_get_boolean (value)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_aid_icon_class_init (SysprofAidIconClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); - - object_class->finalize = sysprof_aid_icon_finalize; - object_class->get_property = sysprof_aid_icon_get_property; - object_class->set_property = sysprof_aid_icon_set_property; - - properties [PROP_AID] = - g_param_spec_object ("aid", - "Aid", - "The aid for the icon", - SYSPROF_TYPE_AID, - (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); - - properties [PROP_SELECTED] = - g_param_spec_boolean ("selected", - "Selected", - "If the item is selected", - FALSE, - (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_properties (object_class, N_PROPS, properties); - - gtk_widget_class_set_css_name (widget_class, "sysprofaidicon"); - gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/sysprof/ui/sysprof-aid-icon.ui"); - gtk_widget_class_bind_template_child (widget_class, SysprofAidIcon, check); - gtk_widget_class_bind_template_child (widget_class, SysprofAidIcon, image); - gtk_widget_class_bind_template_child (widget_class, SysprofAidIcon, label); -} - -static void -sysprof_aid_icon_init (SysprofAidIcon *self) -{ - gtk_widget_init_template (GTK_WIDGET (self)); -} - -void -sysprof_aid_icon_toggle (SysprofAidIcon *self) -{ - g_return_if_fail (SYSPROF_IS_AID_ICON (self)); - - gtk_widget_set_visible (GTK_WIDGET (self->check), - !gtk_widget_get_visible (GTK_WIDGET (self->check))); -} diff --git a/src/libsysprof-ui/sysprof-aid-icon.h b/src/libsysprof-ui/sysprof-aid-icon.h deleted file mode 100644 index 20f53f68..00000000 --- a/src/libsysprof-ui/sysprof-aid-icon.h +++ /dev/null @@ -1,39 +0,0 @@ -/* sysprof-aid-icon.h - * - * Copyright 2019 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 - -#include "sysprof-aid.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_AID_ICON (sysprof_aid_icon_get_type()) - -G_DECLARE_FINAL_TYPE (SysprofAidIcon, sysprof_aid_icon, SYSPROF, AID_ICON, GtkFlowBoxChild) - -GtkWidget *sysprof_aid_icon_new (SysprofAid *aid); -SysprofAid *sysprof_aid_icon_get_aid (SysprofAidIcon *self); -void sysprof_aid_icon_toggle (SysprofAidIcon *self); -gboolean sysprof_aid_icon_is_selected (SysprofAidIcon *self); - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-aid-icon.ui b/src/libsysprof-ui/sysprof-aid-icon.ui deleted file mode 100644 index 491cb405..00000000 --- a/src/libsysprof-ui/sysprof-aid-icon.ui +++ /dev/null @@ -1,41 +0,0 @@ - - - - - diff --git a/src/libsysprof-ui/sysprof-aid.c b/src/libsysprof-ui/sysprof-aid.c deleted file mode 100644 index 1bf3a489..00000000 --- a/src/libsysprof-ui/sysprof-aid.c +++ /dev/null @@ -1,353 +0,0 @@ -/* sysprof-aid.c - * - * Copyright 2019 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 - */ - -#define G_LOG_DOMAIN "sysprof-aid" - -#include "config.h" - -#include - -#include "sysprof-aid.h" - -typedef struct -{ - GPtrArray *sources; - gchar *display_name; - GIcon *icon; -} SysprofAidPrivate; - -static void buildable_iface_init (GtkBuildableIface *iface); - -G_DEFINE_TYPE_WITH_CODE (SysprofAid, sysprof_aid, G_TYPE_OBJECT, - G_ADD_PRIVATE (SysprofAid) - G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, buildable_iface_init)) - -enum { - PROP_0, - PROP_DISPLAY_NAME, - PROP_ICON, - PROP_ICON_NAME, - N_PROPS -}; - -static GParamSpec *properties [N_PROPS]; - -static void -sysprof_aid_real_present_async (SysprofAid *self, - SysprofCaptureReader *reader, - SysprofDisplay *display, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - g_task_report_new_error (self, callback, user_data, - sysprof_aid_real_present_async, - G_IO_ERROR, - G_IO_ERROR_NOT_SUPPORTED, - "Not supported"); -} - -static gboolean -sysprof_aid_real_present_finish (SysprofAid *self, - GAsyncResult *result, - GError **error) -{ - return g_task_propagate_boolean (G_TASK (result), error); -} - -static void -sysprof_aid_finalize (GObject *object) -{ - SysprofAid *self = (SysprofAid *)object; - SysprofAidPrivate *priv = sysprof_aid_get_instance_private (self); - - g_clear_pointer (&priv->sources, g_ptr_array_unref); - g_clear_pointer (&priv->display_name, g_free); - g_clear_object (&priv->icon); - - G_OBJECT_CLASS (sysprof_aid_parent_class)->finalize (object); -} - -static void -sysprof_aid_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - SysprofAid *self = SYSPROF_AID (object); - - switch (prop_id) - { - case PROP_DISPLAY_NAME: - g_value_set_string (value, sysprof_aid_get_display_name (self)); - break; - - case PROP_ICON: - g_value_set_object (value, sysprof_aid_get_icon (self)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_aid_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - SysprofAid *self = SYSPROF_AID (object); - - switch (prop_id) - { - case PROP_DISPLAY_NAME: - sysprof_aid_set_display_name (self, g_value_get_string (value)); - break; - - case PROP_ICON: - sysprof_aid_set_icon (self, g_value_get_object (value)); - break; - - case PROP_ICON_NAME: - sysprof_aid_set_icon_name (self, g_value_get_string (value)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_aid_class_init (SysprofAidClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->finalize = sysprof_aid_finalize; - object_class->get_property = sysprof_aid_get_property; - object_class->set_property = sysprof_aid_set_property; - - klass->present_async = sysprof_aid_real_present_async; - klass->present_finish = sysprof_aid_real_present_finish; - - properties [PROP_DISPLAY_NAME] = - g_param_spec_string ("display-name", - "Display Name", - "Display Name", - NULL, - (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); - - properties [PROP_ICON_NAME] = - g_param_spec_string ("icon-name", - "Icon Name", - "Icon Name", - NULL, - (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); - - properties [PROP_ICON] = - g_param_spec_object ("icon", - "Icon", - "The icon to display", - G_TYPE_ICON, - (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_properties (object_class, N_PROPS, properties); -} - -static void -sysprof_aid_init (SysprofAid *self G_GNUC_UNUSED) -{ -} - -/** - * sysprof_aid_get_display_name: - * @self: a #SysprofAid - * - * Gets the display name as it should be shown to the user. - * - * Returns: a string containing the display name - * - * Since: 3.34 - */ -const gchar * -sysprof_aid_get_display_name (SysprofAid *self) -{ - SysprofAidPrivate *priv = sysprof_aid_get_instance_private (self); - - g_return_val_if_fail (SYSPROF_IS_AID (self), NULL); - - return priv->display_name; -} - -/** - * sysprof_aid_get_icon: - * - * Gets the icon for the aid. - * - * Returns: (transfer none) (nullable): a #GIcon or %NULL - * - * Since: 3.34 - */ -GIcon * -sysprof_aid_get_icon (SysprofAid *self) -{ - SysprofAidPrivate *priv = sysprof_aid_get_instance_private (self); - - g_return_val_if_fail (SYSPROF_IS_AID (self), NULL); - - return priv->icon; -} - -void -sysprof_aid_set_icon (SysprofAid *self, - GIcon *icon) -{ - SysprofAidPrivate *priv = sysprof_aid_get_instance_private (self); - - g_return_if_fail (SYSPROF_IS_AID (self)); - - if (g_set_object (&priv->icon, icon)) - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ICON]); -} - -void -sysprof_aid_set_icon_name (SysprofAid *self, - const gchar *icon_name) -{ - g_autoptr(GIcon) icon = NULL; - - g_return_if_fail (SYSPROF_IS_AID (self)); - - if (icon_name != NULL) - icon = g_themed_icon_new (icon_name); - - sysprof_aid_set_icon (self, icon); -} - -void -sysprof_aid_set_display_name (SysprofAid *self, - const gchar *display_name) -{ - SysprofAidPrivate *priv = sysprof_aid_get_instance_private (self); - - g_return_if_fail (SYSPROF_IS_AID (self)); - - if (g_strcmp0 (display_name, priv->display_name) != 0) - { - g_free (priv->display_name); - priv->display_name = g_strdup (display_name); - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_DISPLAY_NAME]); - } -} - -void -sysprof_aid_prepare (SysprofAid *self, - SysprofProfiler *profiler) -{ - SysprofAidPrivate *priv = sysprof_aid_get_instance_private (self); - - g_return_if_fail (SYSPROF_IS_AID (self)); - g_return_if_fail (SYSPROF_IS_PROFILER (profiler)); - - if (priv->sources != NULL) - { - for (guint i = 0; i < priv->sources->len; i++) - { - SysprofSource *source = g_ptr_array_index (priv->sources, i); - - sysprof_profiler_add_source (profiler, source); - } - - if (priv->sources->len > 0) - g_ptr_array_remove_range (priv->sources, 0, priv->sources->len); - } - - if (SYSPROF_AID_GET_CLASS (self)->prepare) - SYSPROF_AID_GET_CLASS (self)->prepare (self, profiler); -} - -static void -sysprof_aid_add_child (GtkBuildable *buildable, - GtkBuilder *builder, - GObject *object, - const gchar *type G_GNUC_UNUSED) -{ - SysprofAid *self = (SysprofAid *)buildable; - SysprofAidPrivate *priv = sysprof_aid_get_instance_private (self); - - g_assert (SYSPROF_IS_AID (self)); - g_assert (GTK_IS_BUILDER (builder)); - g_assert (G_IS_OBJECT (object)); - - if (SYSPROF_IS_SOURCE (object)) - { - if (priv->sources == NULL) - priv->sources = g_ptr_array_new_with_free_func (g_object_unref); - g_ptr_array_add (priv->sources, g_object_ref (object)); - return; - } - - g_warning ("Unsupported child type of %s: %s", - G_OBJECT_TYPE_NAME (self), - G_OBJECT_TYPE_NAME (object)); -} - -static void -buildable_iface_init (GtkBuildableIface *iface) -{ - iface->add_child = sysprof_aid_add_child; -} - -SysprofAid * -sysprof_aid_new (const gchar *display_name, - const gchar *icon_name) -{ - return g_object_new (SYSPROF_TYPE_AID, - "display-aid", display_name, - "icon-name", icon_name, - NULL); -} - -void -sysprof_aid_present_async (SysprofAid *self, - SysprofCaptureReader *reader, - SysprofDisplay *display, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - g_return_if_fail (SYSPROF_IS_AID (self)); - g_return_if_fail (reader != NULL); - g_return_if_fail (SYSPROF_IS_DISPLAY (display)); - g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); - - SYSPROF_AID_GET_CLASS (self)->present_async (self, reader, display, cancellable, callback, user_data); -} - -gboolean -sysprof_aid_present_finish (SysprofAid *self, - GAsyncResult *result, - GError **error) -{ - g_return_val_if_fail (SYSPROF_IS_AID (self), FALSE); - g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE); - - return SYSPROF_AID_GET_CLASS (self)->present_finish (self, result, error); -} diff --git a/src/libsysprof-ui/sysprof-aid.h b/src/libsysprof-ui/sysprof-aid.h deleted file mode 100644 index 70a16667..00000000 --- a/src/libsysprof-ui/sysprof-aid.h +++ /dev/null @@ -1,76 +0,0 @@ -/* sysprof-aid.h - * - * Copyright 2019 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 - -#include "sysprof-display.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_AID (sysprof_aid_get_type()) - -G_DECLARE_DERIVABLE_TYPE (SysprofAid, sysprof_aid, SYSPROF, AID, GObject) - -struct _SysprofAidClass -{ - GObjectClass parent_class; - - void (*prepare) (SysprofAid *self, - SysprofProfiler *profiler); - void (*present_async) (SysprofAid *self, - SysprofCaptureReader *reader, - SysprofDisplay *display, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); - gboolean (*present_finish) (SysprofAid *self, - GAsyncResult *result, - GError **error); - - /*< private >*/ - gpointer _reserved[16]; -}; - -SysprofAid *sysprof_aid_new (const gchar *display_name, - const gchar *icon_name); -const gchar *sysprof_aid_get_display_name (SysprofAid *self); -void sysprof_aid_set_display_name (SysprofAid *self, - const gchar *display_name); -GIcon *sysprof_aid_get_icon (SysprofAid *self); -void sysprof_aid_set_icon (SysprofAid *self, - GIcon *icon); -void sysprof_aid_set_icon_name (SysprofAid *self, - const gchar *icon_name); -void sysprof_aid_prepare (SysprofAid *self, - SysprofProfiler *profiler); -void sysprof_aid_present_async (SysprofAid *self, - SysprofCaptureReader *reader, - SysprofDisplay *display, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean sysprof_aid_present_finish (SysprofAid *self, - GAsyncResult *result, - GError **error); - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-battery-aid.c b/src/libsysprof-ui/sysprof-battery-aid.c deleted file mode 100644 index 9351200f..00000000 --- a/src/libsysprof-ui/sysprof-battery-aid.c +++ /dev/null @@ -1,242 +0,0 @@ -/* sysprof-battery-aid.c - * - * Copyright 2019 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 - */ - -#define G_LOG_DOMAIN "sysprof-battery-aid" - -#include "config.h" - -#include - -#include "sysprof-color-cycle.h" -#include "sysprof-battery-aid.h" -#include "sysprof-line-visualizer.h" - -struct _SysprofBatteryAid -{ - SysprofAid parent_instance; -}; - -typedef struct -{ - SysprofCaptureCursor *cursor; - SysprofDisplay *display; -} Present; - -G_DEFINE_TYPE (SysprofBatteryAid, sysprof_battery_aid, SYSPROF_TYPE_AID) - -static void -present_free (gpointer data) -{ - Present *p = data; - - g_clear_pointer (&p->cursor, sysprof_capture_cursor_unref); - g_clear_object (&p->display); - g_slice_free (Present, p); -} - -/** - * sysprof_battery_aid_new: - * - * Create a new #SysprofBatteryAid. - * - * Returns: (transfer full): a newly created #SysprofBatteryAid - * - * Since: 3.34 - */ -SysprofAid * -sysprof_battery_aid_new (void) -{ - return g_object_new (SYSPROF_TYPE_BATTERY_AID, NULL); -} - -static void -sysprof_battery_aid_prepare (SysprofAid *self, - SysprofProfiler *profiler) -{ -#ifdef __linux__ - g_autoptr(SysprofSource) source = NULL; - - g_assert (SYSPROF_IS_BATTERY_AID (self)); - g_assert (SYSPROF_IS_PROFILER (profiler)); - - source = sysprof_battery_source_new (); - sysprof_profiler_add_source (profiler, source); -#endif -} - -static bool -collect_battery_counters (const SysprofCaptureFrame *frame, - gpointer user_data) -{ - SysprofCaptureCounterDefine *def = (SysprofCaptureCounterDefine *)frame; - GArray *counters = user_data; - - g_assert (frame != NULL); - g_assert (frame->type == SYSPROF_CAPTURE_FRAME_CTRDEF); - g_assert (counters != NULL); - - for (guint i = 0; i < def->n_counters; i++) - { - const SysprofCaptureCounter *counter = &def->counters[i]; - - if (g_strcmp0 (counter->category, "Battery Charge") == 0) - g_array_append_vals (counters, counter, 1); - } - - return TRUE; -} - -static void -sysprof_battery_aid_present_worker (GTask *task, - gpointer source_object, - gpointer task_data, - GCancellable *cancellable) -{ - Present *present = task_data; - g_autoptr(GArray) counters = NULL; - - g_assert (G_IS_TASK (task)); - g_assert (SYSPROF_IS_BATTERY_AID (source_object)); - g_assert (present != NULL); - g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); - - counters = g_array_new (FALSE, FALSE, sizeof (SysprofCaptureCounter)); - sysprof_capture_cursor_foreach (present->cursor, collect_battery_counters, counters); - g_task_return_pointer (task, - g_steal_pointer (&counters), - (GDestroyNotify) g_array_unref); -} - -static void -sysprof_battery_aid_present_async (SysprofAid *aid, - SysprofCaptureReader *reader, - SysprofDisplay *display, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - static const SysprofCaptureFrameType types[] = { SYSPROF_CAPTURE_FRAME_CTRDEF }; - g_autoptr(SysprofCaptureCondition) condition = NULL; - g_autoptr(SysprofCaptureCursor) cursor = NULL; - g_autoptr(GTask) task = NULL; - Present present; - - g_assert (SYSPROF_IS_BATTERY_AID (aid)); - g_assert (reader != NULL); - g_assert (SYSPROF_IS_DISPLAY (display)); - g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); - - condition = sysprof_capture_condition_new_where_type_in (1, types); - cursor = sysprof_capture_cursor_new (reader); - sysprof_capture_cursor_add_condition (cursor, g_steal_pointer (&condition)); - - present.cursor = g_steal_pointer (&cursor); - present.display = g_object_ref (display); - - task = g_task_new (aid, cancellable, callback, user_data); - g_task_set_source_tag (task, sysprof_battery_aid_present_async); - g_task_set_task_data (task, - g_slice_dup (Present, &present), - present_free); - g_task_run_in_thread (task, sysprof_battery_aid_present_worker); -} - -static gboolean -sysprof_battery_aid_present_finish (SysprofAid *aid, - GAsyncResult *result, - GError **error) -{ - g_autoptr(GArray) counters = NULL; - Present *present; - - g_assert (SYSPROF_IS_AID (aid)); - g_assert (G_IS_TASK (result)); - - present = g_task_get_task_data (G_TASK (result)); - - if ((counters = g_task_propagate_pointer (G_TASK (result), error))) - { - g_autoptr(SysprofColorCycle) cycle = sysprof_color_cycle_new (); - SysprofVisualizerGroup *group; - guint found = 0; - - group = g_object_new (SYSPROF_TYPE_VISUALIZER_GROUP, - "can-focus", TRUE, - "title", _("Battery Charge"), - "visible", TRUE, - NULL); - - for (guint i = 0; i < counters->len; i++) - { - const SysprofCaptureCounter *ctr = &g_array_index (counters, SysprofCaptureCounter, i); - - if (g_strcmp0 (ctr->category, "Battery Charge") == 0) - { - g_autofree gchar *title = NULL; - gboolean is_combined = g_str_equal (ctr->name, "Combined"); - GtkWidget *row; - GdkRGBA rgba; - - if (is_combined) - title = g_strdup (_("Battery Charge (All)")); - else - title = g_strdup_printf ("Battery Charge (%s)", ctr->name); - - row = g_object_new (SYSPROF_TYPE_LINE_VISUALIZER, - "title", title, - "height-request", 35, - "visible", is_combined, - NULL); - sysprof_color_cycle_next (cycle, &rgba); - sysprof_line_visualizer_add_counter (SYSPROF_LINE_VISUALIZER (row), ctr->id, &rgba); - sysprof_visualizer_group_insert (group, - SYSPROF_VISUALIZER (row), - is_combined ? 0 : -1, - !is_combined); - - found++; - } - } - - if (found > 0) - sysprof_display_add_group (present->display, group); - else - g_object_unref (g_object_ref_sink (group)); - } - - return counters != NULL; -} - -static void -sysprof_battery_aid_class_init (SysprofBatteryAidClass *klass) -{ - SysprofAidClass *aid_class = SYSPROF_AID_CLASS (klass); - - aid_class->prepare = sysprof_battery_aid_prepare; - aid_class->present_async = sysprof_battery_aid_present_async; - aid_class->present_finish = sysprof_battery_aid_present_finish; -} - -static void -sysprof_battery_aid_init (SysprofBatteryAid *self) -{ - sysprof_aid_set_display_name (SYSPROF_AID (self), _("Battery")); - sysprof_aid_set_icon_name (SYSPROF_AID (self), "battery-low-charging-symbolic"); -} diff --git a/src/libsysprof-ui/sysprof-battery-aid.h b/src/libsysprof-ui/sysprof-battery-aid.h deleted file mode 100644 index 563793f1..00000000 --- a/src/libsysprof-ui/sysprof-battery-aid.h +++ /dev/null @@ -1,33 +0,0 @@ -/* sysprof-battery-aid.h - * - * Copyright 2019 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-aid.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_BATTERY_AID (sysprof_battery_aid_get_type()) - -G_DECLARE_FINAL_TYPE (SysprofBatteryAid, sysprof_battery_aid, SYSPROF, BATTERY_AID, SysprofAid) - -SysprofAid *sysprof_battery_aid_new (void); - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-cairo.c b/src/libsysprof-ui/sysprof-cairo.c deleted file mode 100644 index 3d8f82a6..00000000 --- a/src/libsysprof-ui/sysprof-cairo.c +++ /dev/null @@ -1,71 +0,0 @@ -/* sysprof-cairo.c - * - * Copyright 2019 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-ui-private.h" - -void -_sysprof_rounded_rectangle (cairo_t *cr, - const GdkRectangle *rect, - gint x_radius, - gint y_radius) -{ - gint x; - gint y; - gint width; - gint height; - gint x1, x2; - gint y1, y2; - gint xr1, xr2; - gint yr1, yr2; - - g_return_if_fail (cr); - g_return_if_fail (rect); - - x = rect->x; - y = rect->y; - width = rect->width; - height = rect->height; - - x1 = x; - x2 = x1 + width; - y1 = y; - y2 = y1 + height; - - x_radius = MIN (x_radius, width / 2.0); - y_radius = MIN (y_radius, width / 2.0); - - xr1 = x_radius; - xr2 = x_radius / 2.0; - yr1 = y_radius; - yr2 = y_radius / 2.0; - - cairo_move_to (cr, x1 + xr1, y1); - cairo_line_to (cr, x2 - xr1, y1); - cairo_curve_to (cr, x2 - xr2, y1, x2, y1 + yr2, x2, y1 + yr1); - cairo_line_to (cr, x2, y2 - yr1); - cairo_curve_to (cr, x2, y2 - yr2, x2 - xr2, y2, x2 - xr1, y2); - cairo_line_to (cr, x1 + xr1, y2); - cairo_curve_to (cr, x1 + xr2, y2, x1, y2 - yr2, x1, y2 - yr1); - cairo_line_to (cr, x1, y1 + yr1); - cairo_curve_to (cr, x1, y1 + yr2, x1 + xr2, y1, x1 + xr1, y1); - cairo_close_path (cr); -} diff --git a/src/libsysprof-ui/sysprof-callgraph-aid.c b/src/libsysprof-ui/sysprof-callgraph-aid.c deleted file mode 100644 index f12be766..00000000 --- a/src/libsysprof-ui/sysprof-callgraph-aid.c +++ /dev/null @@ -1,275 +0,0 @@ -/* sysprof-callgraph-aid.c - * - * Copyright 2019 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 - */ - -#define G_LOG_DOMAIN "sysprof-callgraph-aid" - -#include "config.h" - -#include - -#include "sysprof-callgraph-aid.h" -#include "sysprof-callgraph-page.h" -#include "sysprof-depth-visualizer.h" - -struct _SysprofCallgraphAid -{ - SysprofAid parent_instance; -}; - -typedef struct -{ - SysprofCaptureCursor *cursor; - SysprofDisplay *display; - guint has_samples : 1; -} Present; - -G_DEFINE_TYPE (SysprofCallgraphAid, sysprof_callgraph_aid, SYSPROF_TYPE_AID) - -static void -present_free (gpointer data) -{ - Present *p = data; - - g_clear_pointer (&p->cursor, sysprof_capture_cursor_unref); - g_clear_object (&p->display); - g_slice_free (Present, p); -} - -static void -on_group_activated_cb (SysprofVisualizerGroup *group, - SysprofPage *page) -{ - SysprofDisplay *display; - - g_assert (SYSPROF_IS_VISUALIZER_GROUP (group)); - g_assert (SYSPROF_IS_PAGE (page)); - - display = SYSPROF_DISPLAY (gtk_widget_get_ancestor (GTK_WIDGET (page), SYSPROF_TYPE_DISPLAY)); - sysprof_display_set_visible_page (display, page); -} - -/** - * sysprof_callgraph_aid_new: - * - * Create a new #SysprofCallgraphAid. - * - * Returns: (transfer full): a newly created #SysprofCallgraphAid - * - * Since: 3.34 - */ -SysprofAid * -sysprof_callgraph_aid_new (void) -{ - return g_object_new (SYSPROF_TYPE_CALLGRAPH_AID, NULL); -} - -static void -sysprof_callgraph_aid_prepare (SysprofAid *self, - SysprofProfiler *profiler) -{ - g_assert (SYSPROF_IS_CALLGRAPH_AID (self)); - g_assert (SYSPROF_IS_PROFILER (profiler)); - -#ifdef __linux__ - { - const GPid *pids; - guint n_pids; - - if ((pids = sysprof_profiler_get_pids (profiler, &n_pids))) - { - for (guint i = 0; i < n_pids; i++) - { - g_autoptr(SysprofSource) source = NULL; - - source = sysprof_perf_source_new (); - sysprof_perf_source_set_target_pid (SYSPROF_PERF_SOURCE (source), pids[i]); - sysprof_profiler_add_source (profiler, source); - } - } - else - { - g_autoptr(SysprofSource) source = NULL; - - source = sysprof_perf_source_new (); - sysprof_profiler_add_source (profiler, source); - } - } -#endif -} - -static bool -discover_samples_cb (const SysprofCaptureFrame *frame, - gpointer user_data) -{ - Present *p = user_data; - - g_assert (frame != NULL); - g_assert (p != NULL); - - if (frame->type == SYSPROF_CAPTURE_FRAME_SAMPLE) - { - p->has_samples = TRUE; - return FALSE; - } - - return TRUE; -} - -static void -sysprof_callgraph_aid_present_worker (GTask *task, - gpointer source_object, - gpointer task_data, - GCancellable *cancellable) -{ - Present *p = task_data; - - g_assert (G_IS_TASK (task)); - g_assert (SYSPROF_IS_CALLGRAPH_AID (source_object)); - g_assert (p != NULL); - g_assert (p->cursor != NULL); - g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); - - /* If we find a sample frame, then we should enable the callgraph - * and stack visualizers. - */ - sysprof_capture_cursor_foreach (p->cursor, discover_samples_cb, p); - g_task_return_boolean (task, TRUE); -} - -static void -sysprof_callgraph_aid_present_async (SysprofAid *aid, - SysprofCaptureReader *reader, - SysprofDisplay *display, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - static const SysprofCaptureFrameType types[] = { SYSPROF_CAPTURE_FRAME_SAMPLE }; - g_autoptr(SysprofCaptureCondition) condition = NULL; - g_autoptr(SysprofCaptureCursor) cursor = NULL; - g_autoptr(GTask) task = NULL; - Present present; - - g_assert (SYSPROF_IS_CALLGRAPH_AID (aid)); - g_assert (reader != NULL); - g_assert (SYSPROF_IS_DISPLAY (display)); - g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); - - condition = sysprof_capture_condition_new_where_type_in (1, types); - cursor = sysprof_capture_cursor_new (reader); - sysprof_capture_cursor_add_condition (cursor, g_steal_pointer (&condition)); - - present.cursor = g_steal_pointer (&cursor); - present.display = g_object_ref (display); - - task = g_task_new (aid, cancellable, callback, user_data); - g_task_set_source_tag (task, sysprof_callgraph_aid_present_async); - g_task_set_task_data (task, - g_slice_dup (Present, &present), - present_free); - g_task_run_in_thread (task, sysprof_callgraph_aid_present_worker); -} - -static gboolean -sysprof_callgraph_aid_present_finish (SysprofAid *aid, - GAsyncResult *result, - GError **error) -{ - Present *p; - - g_assert (SYSPROF_IS_CALLGRAPH_AID (aid)); - g_assert (G_IS_TASK (result)); - - p = g_task_get_task_data (G_TASK (result)); - - if (p->has_samples) - { - SysprofVisualizerGroup *group; - SysprofVisualizer *depth; - SysprofPage *page; - - group = g_object_new (SYSPROF_TYPE_VISUALIZER_GROUP, - "can-focus", TRUE, - "has-page", TRUE, - "priority", -500, - "title", _("Stack Traces"), - "visible", TRUE, - NULL); - - depth = sysprof_depth_visualizer_new (SYSPROF_DEPTH_VISUALIZER_COMBINED); - g_object_set (depth, - "title", _("Stack Traces"), - "height-request", 35, - "visible", TRUE, - NULL); - sysprof_visualizer_group_insert (group, depth, 0, FALSE); - - depth = sysprof_depth_visualizer_new (SYSPROF_DEPTH_VISUALIZER_KERNEL_ONLY); - g_object_set (depth, - "title", _("Stack Traces (In Kernel)"), - "height-request", 35, - "visible", FALSE, - NULL); - sysprof_visualizer_group_insert (group, depth, 1, TRUE); - - depth = sysprof_depth_visualizer_new (SYSPROF_DEPTH_VISUALIZER_USER_ONLY); - g_object_set (depth, - "title", _("Stack Traces (In User)"), - "height-request", 35, - "visible", FALSE, - NULL); - sysprof_visualizer_group_insert (group, depth, 2, TRUE); - - sysprof_display_add_group (p->display, group); - - page = g_object_new (SYSPROF_TYPE_CALLGRAPH_PAGE, - "title", _("Callgraph"), - "vexpand", TRUE, - "visible", TRUE, - NULL); - sysprof_display_add_page (p->display, page); - sysprof_display_set_visible_page (p->display, page); - - g_signal_connect_object (group, - "group-activated", - G_CALLBACK (on_group_activated_cb), - page, - 0); - } - - return g_task_propagate_boolean (G_TASK (result), error); -} - -static void -sysprof_callgraph_aid_class_init (SysprofCallgraphAidClass *klass) -{ - SysprofAidClass *aid_class = SYSPROF_AID_CLASS (klass); - - aid_class->prepare = sysprof_callgraph_aid_prepare; - aid_class->present_async = sysprof_callgraph_aid_present_async; - aid_class->present_finish = sysprof_callgraph_aid_present_finish; -} - -static void -sysprof_callgraph_aid_init (SysprofCallgraphAid *self) -{ - sysprof_aid_set_display_name (SYSPROF_AID (self), _("Callgraph")); - sysprof_aid_set_icon_name (SYSPROF_AID (self), "org.gnome.Sysprof-symbolic"); -} diff --git a/src/libsysprof-ui/sysprof-callgraph-aid.h b/src/libsysprof-ui/sysprof-callgraph-aid.h deleted file mode 100644 index 3fdde80a..00000000 --- a/src/libsysprof-ui/sysprof-callgraph-aid.h +++ /dev/null @@ -1,33 +0,0 @@ -/* sysprof-callgraph-aid.h - * - * Copyright 2019 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-aid.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_CALLGRAPH_AID (sysprof_callgraph_aid_get_type()) - -G_DECLARE_FINAL_TYPE (SysprofCallgraphAid, sysprof_callgraph_aid, SYSPROF, CALLGRAPH_AID, SysprofAid) - -SysprofAid *sysprof_callgraph_aid_new (void); - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-callgraph-page.c b/src/libsysprof-ui/sysprof-callgraph-page.c deleted file mode 100644 index 0cbabbb4..00000000 --- a/src/libsysprof-ui/sysprof-callgraph-page.c +++ /dev/null @@ -1,1299 +0,0 @@ -/* sysprof-callgraph-page.c - * - * Copyright 2016-2019 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 - */ - -/* Sysprof -- Sampling, systemwide CPU profiler - * Copyright 2004, Red Hat, Inc. - * Copyright 2004, 2005, 2006, Soeren Sandmann - * - * 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 2 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, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include "config.h" - -#include - -#include "../stackstash.h" - -#include "egg-paned-private.h" - -#include "sysprof-callgraph-page.h" -#include "sysprof-cell-renderer-percent.h" - -typedef struct -{ - SysprofCallgraphProfile *profile; - - GtkTreeView *callers_view; - GtkTreeView *functions_view; - GtkTreeView *descendants_view; - GtkTreeViewColumn *descendants_name_column; - GtkStack *stack; - GtkWidget *empty_state; - GtkWidget *loading_state; - GtkWidget *callgraph; - - GQueue *history; - - guint profile_size; - guint loading; -} SysprofCallgraphPagePrivate; - -G_DEFINE_TYPE_WITH_PRIVATE (SysprofCallgraphPage, sysprof_callgraph_page, SYSPROF_TYPE_PAGE) - -enum { - PROP_0, - PROP_PROFILE, - N_PROPS -}; - -enum { - GO_PREVIOUS, - N_SIGNALS -}; - -enum { - COLUMN_NAME, - COLUMN_SELF, - COLUMN_TOTAL, - COLUMN_POINTER, - COLUMN_HITS, -}; - -static void sysprof_callgraph_page_update_descendants (SysprofCallgraphPage *self, - StackNode *node); - -static GParamSpec *properties [N_PROPS]; -static guint signals [N_SIGNALS]; - -static guint -sysprof_callgraph_page_get_profile_size (SysprofCallgraphPage *self) -{ - SysprofCallgraphPagePrivate *priv = sysprof_callgraph_page_get_instance_private (self); - StackStash *stash; - StackNode *node; - guint size = 0; - - g_assert (SYSPROF_IS_CALLGRAPH_PAGE (self)); - - if (priv->profile_size != 0) - return priv->profile_size; - - if (priv->profile == NULL) - return 0; - - if (NULL == (stash = sysprof_callgraph_profile_get_stash (priv->profile))) - return 0; - - for (node = stack_stash_get_root (stash); node != NULL; node = node->siblings) - size += node->total; - - priv->profile_size = size; - - return size; -} - -static void -build_functions_store (StackNode *node, - gpointer user_data) -{ - struct { - GtkListStore *store; - gdouble profile_size; - } *state = user_data; - GtkTreeIter iter; - const StackNode *n; - guint size = 0; - guint total = 0; - - g_assert (state != NULL); - g_assert (GTK_IS_LIST_STORE (state->store)); - - for (n = node; n != NULL; n = n->next) - { - size += n->size; - if (n->toplevel) - total += n->total; - } - - gtk_list_store_append (state->store, &iter); - gtk_list_store_set (state->store, &iter, - COLUMN_NAME, U64_TO_POINTER(node->data), - COLUMN_SELF, 100.0 * size / state->profile_size, - COLUMN_TOTAL, 100.0 * total / state->profile_size, - COLUMN_POINTER, node, - -1); - -} - -static void -sysprof_callgraph_page_load (SysprofCallgraphPage *self, - SysprofCallgraphProfile *profile) -{ - SysprofCallgraphPagePrivate *priv = sysprof_callgraph_page_get_instance_private (self); - GtkListStore *functions; - StackStash *stash; - StackNode *n; - GtkTreeIter iter; - struct { - GtkListStore *store; - gdouble profile_size; - } state = { 0 }; - - g_assert (SYSPROF_IS_CALLGRAPH_PAGE (self)); - g_assert (SYSPROF_IS_CALLGRAPH_PROFILE (profile)); - - /* - * TODO: This is probably the type of thing we want to do off the main - * thread. We should be able to build the tree models off thread - * and then simply apply them on the main thread. - * - * In the mean time, we should set the state of the widget to - * insensitive and give some indication of loading progress. - */ - - if (!g_set_object (&priv->profile, profile)) - return; - - if (sysprof_callgraph_profile_is_empty (profile)) - return; - - stash = sysprof_callgraph_profile_get_stash (profile); - - for (n = stack_stash_get_root (stash); n; n = n->siblings) - state.profile_size += n->total; - - functions = gtk_list_store_new (4, G_TYPE_STRING, G_TYPE_DOUBLE, G_TYPE_DOUBLE, G_TYPE_POINTER); - - state.store = functions; - stack_stash_foreach_by_address (stash, build_functions_store, &state); - - gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (functions), - COLUMN_TOTAL, - GTK_SORT_DESCENDING); - - gtk_tree_view_set_model (priv->functions_view, GTK_TREE_MODEL (functions)); - gtk_tree_view_set_model (priv->callers_view, NULL); - gtk_tree_view_set_model (priv->descendants_view, NULL); - - if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (functions), &iter)) - { - GtkTreeSelection *selection; - - selection = gtk_tree_view_get_selection (priv->functions_view); - gtk_tree_selection_select_iter (selection, &iter); - } - - gtk_stack_set_visible_child (priv->stack, priv->callgraph); - - g_clear_object (&functions); -} - -void -_sysprof_callgraph_page_set_failed (SysprofCallgraphPage *self) -{ - SysprofCallgraphPagePrivate *priv = sysprof_callgraph_page_get_instance_private (self); - - g_return_if_fail (SYSPROF_IS_CALLGRAPH_PAGE (self)); - - gtk_stack_set_visible_child (priv->stack, priv->empty_state); -} - -static void -sysprof_callgraph_page_unload (SysprofCallgraphPage *self) -{ - SysprofCallgraphPagePrivate *priv = sysprof_callgraph_page_get_instance_private (self); - - g_assert (SYSPROF_IS_CALLGRAPH_PAGE (self)); - g_assert (SYSPROF_IS_CALLGRAPH_PROFILE (priv->profile)); - - g_queue_clear (priv->history); - g_clear_object (&priv->profile); - priv->profile_size = 0; - - gtk_tree_view_set_model (priv->callers_view, NULL); - gtk_tree_view_set_model (priv->functions_view, NULL); - gtk_tree_view_set_model (priv->descendants_view, NULL); - - gtk_stack_set_visible_child (priv->stack, priv->empty_state); -} - -/** - * sysprof_callgraph_page_get_profile: - * - * Returns: (transfer none): An #SysprofCallgraphProfile. - */ -SysprofCallgraphProfile * -sysprof_callgraph_page_get_profile (SysprofCallgraphPage *self) -{ - SysprofCallgraphPagePrivate *priv = sysprof_callgraph_page_get_instance_private (self); - - g_return_val_if_fail (SYSPROF_IS_CALLGRAPH_PAGE (self), NULL); - - return priv->profile; -} - -void -sysprof_callgraph_page_set_profile (SysprofCallgraphPage *self, - SysprofCallgraphProfile *profile) -{ - SysprofCallgraphPagePrivate *priv = sysprof_callgraph_page_get_instance_private (self); - - g_return_if_fail (SYSPROF_IS_CALLGRAPH_PAGE (self)); - g_return_if_fail (!profile || SYSPROF_IS_CALLGRAPH_PROFILE (profile)); - - if (profile != priv->profile) - { - if (priv->profile) - sysprof_callgraph_page_unload (self); - - if (profile) - sysprof_callgraph_page_load (self, profile); - - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_PROFILE]); - } -} - -static void -sysprof_callgraph_page_expand_descendants (SysprofCallgraphPage *self) -{ - SysprofCallgraphPagePrivate *priv = sysprof_callgraph_page_get_instance_private (self); - GtkTreeModel *model; - GList *all_paths = NULL; - GtkTreePath *first_path; - GtkTreeIter iter; - gdouble top_value = 0; - gint max_rows = 40; /* FIXME */ - gint n_rows; - - g_assert (SYSPROF_IS_CALLGRAPH_PAGE (self)); - - model = gtk_tree_view_get_model (priv->descendants_view); - first_path = gtk_tree_path_new_first (); - all_paths = g_list_prepend (all_paths, first_path); - n_rows = 1; - - gtk_tree_model_get_iter (model, &iter, first_path); - gtk_tree_model_get (model, &iter, - COLUMN_TOTAL, &top_value, - -1); - - while ((all_paths != NULL) && (n_rows < max_rows)) - { - GtkTreeIter best_iter; - GtkTreePath *best_path = NULL; - GList *list; - gdouble best_value = 0.0; - gint n_children; - gint i; - - for (list = all_paths; list != NULL; list = list->next) - { - GtkTreePath *path = list->data; - - g_assert (path != NULL); - - if (gtk_tree_model_get_iter (model, &iter, path)) - { - gdouble value; - - gtk_tree_model_get (model, &iter, - COLUMN_TOTAL, &value, - -1); - - if (value >= best_value) - { - best_value = value; - best_path = path; - best_iter = iter; - } - } - } - - n_children = gtk_tree_model_iter_n_children (model, &best_iter); - - if ((n_children > 0) && - ((best_value / top_value) > 0.04) && - ((n_children + gtk_tree_path_get_depth (best_path)) / (gdouble)max_rows) < (best_value / top_value)) - { - gtk_tree_view_expand_row (priv->descendants_view, best_path, FALSE); - n_rows += n_children; - - if (gtk_tree_path_get_depth (best_path) < 4) - { - GtkTreePath *path; - - path = gtk_tree_path_copy (best_path); - gtk_tree_path_down (path); - - for (i = 0; i < n_children; i++) - { - all_paths = g_list_prepend (all_paths, path); - - path = gtk_tree_path_copy (path); - gtk_tree_path_next (path); - } - - gtk_tree_path_free (path); - } - } - - all_paths = g_list_remove (all_paths, best_path); - - /* Always expand at least once */ - if ((all_paths == NULL) && (n_rows == 1)) - gtk_tree_view_expand_row (priv->descendants_view, best_path, FALSE); - - gtk_tree_path_free (best_path); - } - - g_list_free_full (all_paths, (GDestroyNotify)gtk_tree_path_free); -} - -typedef struct -{ - StackNode *node; - const gchar *name; - guint self; - guint total; -} Caller; - -static Caller * -caller_new (StackNode *node) -{ - Caller *c; - - c = g_slice_new (Caller); - c->name = U64_TO_POINTER (node->data); - c->self = 0; - c->total = 0; - c->node = node; - - return c; -} - -static void -caller_free (gpointer data) -{ - Caller *c = data; - g_slice_free (Caller, c); -} - -static void -sysprof_callgraph_page_function_selection_changed (SysprofCallgraphPage *self, - GtkTreeSelection *selection) -{ - SysprofCallgraphPagePrivate *priv = sysprof_callgraph_page_get_instance_private (self); - GtkTreeModel *model = NULL; - GtkTreeIter iter; - GtkListStore *callers_store; - g_autoptr(GHashTable) callers = NULL; - g_autoptr(GHashTable) processed = NULL; - StackNode *callees = NULL; - StackNode *node; - - g_assert (SYSPROF_IS_CALLGRAPH_PAGE (self)); - g_assert (GTK_IS_TREE_SELECTION (selection)); - - if (!gtk_tree_selection_get_selected (selection, &model, &iter)) - { - gtk_tree_view_set_model (priv->callers_view, NULL); - gtk_tree_view_set_model (priv->descendants_view, NULL); - return; - } - - gtk_tree_model_get (model, &iter, - COLUMN_POINTER, &callees, - -1); - - sysprof_callgraph_page_update_descendants (self, callees); - - callers_store = gtk_list_store_new (4, - G_TYPE_STRING, - G_TYPE_DOUBLE, - G_TYPE_DOUBLE, - G_TYPE_POINTER); - - callers = g_hash_table_new_full (NULL, NULL, NULL, caller_free); - processed = g_hash_table_new (NULL, NULL); - - for (node = callees; node != NULL; node = node->next) - { - Caller *c; - - if (!node->parent) - continue; - - c = g_hash_table_lookup (callers, U64_TO_POINTER (node->parent->data)); - - if (c == NULL) - { - c = caller_new (node->parent); - g_hash_table_insert (callers, (gpointer)c->name, c); - } - } - - for (node = callees; node != NULL; node = node->next) - { - StackNode *top_caller = node->parent; - StackNode *top_callee = node; - StackNode *n; - Caller *c; - - if (!node->parent) - continue; - - /* - * We could have a situation where the function was called in a - * reentrant fashion, so we want to take the top-most match in the - * stack. - */ - for (n = node; n && n->parent; n = n->parent) - { - if (n->data == node->data && n->parent->data == node->parent->data) - { - top_caller = n->parent; - top_callee = n; - } - } - - c = g_hash_table_lookup (callers, U64_TO_POINTER (node->parent->data)); - - g_assert (c != NULL); - - if (!g_hash_table_lookup (processed, top_caller)) - { - c->total += top_callee->total; - g_hash_table_insert (processed, top_caller, top_caller); - } - - c->self += node->size; - } - - { - GHashTableIter hiter; - gpointer key, value; - guint size = 0; - - size = MAX (1, sysprof_callgraph_page_get_profile_size (self)); - - g_hash_table_iter_init (&hiter, callers); - - while (g_hash_table_iter_next (&hiter, &key, &value)) - { - Caller *c = value; - - gtk_list_store_append (callers_store, &iter); - gtk_list_store_set (callers_store, &iter, - COLUMN_NAME, c->name, - COLUMN_SELF, c->self * 100.0 / size, - COLUMN_TOTAL, c->total * 100.0 / size, - COLUMN_POINTER, c->node, - -1); - } - } - - gtk_tree_view_set_model (priv->callers_view, GTK_TREE_MODEL (callers_store)); - gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (callers_store), - COLUMN_TOTAL, - GTK_SORT_DESCENDING); - - g_clear_object (&callers_store); -} - -static void -sysprof_callgraph_page_set_node (SysprofCallgraphPage *self, - StackNode *node) -{ - SysprofCallgraphPagePrivate *priv = sysprof_callgraph_page_get_instance_private (self); - GtkTreeModel *model; - GtkTreeIter iter; - - g_assert (SYSPROF_IS_CALLGRAPH_PAGE (self)); - g_assert (node != NULL); - - if (priv->profile == NULL) - return; - - model = gtk_tree_view_get_model (priv->functions_view); - - if (gtk_tree_model_get_iter_first (model, &iter)) - { - do - { - StackNode *item = NULL; - - gtk_tree_model_get (model, &iter, - COLUMN_POINTER, &item, - -1); - - if (item != NULL && item->data == node->data) - { - GtkTreeSelection *selection; - - selection = gtk_tree_view_get_selection (priv->functions_view); - gtk_tree_selection_select_iter (selection, &iter); - - break; - } - } - while (gtk_tree_model_iter_next (model, &iter)); - } -} - -static void -sysprof_callgraph_page_descendant_activated (SysprofCallgraphPage *self, - GtkTreePath *path, - GtkTreeViewColumn *column, - GtkTreeView *tree_view) -{ - GtkTreeModel *model; - StackNode *node = NULL; - GtkTreeIter iter; - - g_assert (SYSPROF_IS_CALLGRAPH_PAGE (self)); - g_assert (GTK_IS_TREE_VIEW (tree_view)); - g_assert (path != NULL); - g_assert (GTK_IS_TREE_VIEW_COLUMN (column)); - - model = gtk_tree_view_get_model (tree_view); - - if (!gtk_tree_model_get_iter (model, &iter, path)) - return; - - gtk_tree_model_get (model, &iter, - COLUMN_POINTER, &node, - -1); - - if (node != NULL) - sysprof_callgraph_page_set_node (self, node); -} - -static void -sysprof_callgraph_page_caller_activated (SysprofCallgraphPage *self, - GtkTreePath *path, - GtkTreeViewColumn *column, - GtkTreeView *tree_view) -{ - GtkTreeModel *model; - StackNode *node = NULL; - GtkTreeIter iter; - - g_assert (SYSPROF_IS_CALLGRAPH_PAGE (self)); - g_assert (GTK_IS_TREE_VIEW (tree_view)); - g_assert (path != NULL); - g_assert (GTK_IS_TREE_VIEW_COLUMN (column)); - - model = gtk_tree_view_get_model (tree_view); - - if (!gtk_tree_model_get_iter (model, &iter, path)) - return; - - gtk_tree_model_get (model, &iter, - COLUMN_POINTER, &node, - -1); - - if (node != NULL) - sysprof_callgraph_page_set_node (self, node); -} - -static void -sysprof_callgraph_page_tag_data_func (GtkTreeViewColumn *column, - GtkCellRenderer *cell, - GtkTreeModel *model, - GtkTreeIter *iter, - gpointer data) -{ - SysprofCallgraphPage *self = data; - SysprofCallgraphPagePrivate *priv = sysprof_callgraph_page_get_instance_private (self); - StackNode *node = NULL; - const gchar *str = NULL; - - if (priv->profile == NULL) - return; - - gtk_tree_model_get (model, iter, COLUMN_POINTER, &node, -1); - - if (node && node->data) - { - GQuark tag; - - tag = sysprof_callgraph_profile_get_tag (priv->profile, GSIZE_TO_POINTER (node->data)); - if (tag != 0) - str = g_quark_to_string (tag); - } - - g_object_set (cell, "text", str, NULL); -} - -static void -sysprof_callgraph_page_real_go_previous (SysprofCallgraphPage *self) -{ - SysprofCallgraphPagePrivate *priv = sysprof_callgraph_page_get_instance_private (self); - StackNode *node; - - g_assert (SYSPROF_IS_CALLGRAPH_PAGE (self)); - - node = g_queue_pop_head (priv->history); - - if (NULL != (node = g_queue_peek_head (priv->history))) - sysprof_callgraph_page_set_node (self, node); -} - -static gboolean -descendants_view_move_cursor_cb (GtkTreeView *descendants_view, - GtkMovementStep step, - int direction, - gboolean extend, - gboolean modify, - gpointer user_data) -{ - if (step == GTK_MOVEMENT_VISUAL_POSITIONS) - { - GtkTreePath *path; - - gtk_tree_view_get_cursor (descendants_view, &path, NULL); - - if (direction == 1) - { - gtk_tree_view_expand_row (descendants_view, path, FALSE); - g_signal_stop_emission_by_name (descendants_view, "move-cursor"); - return FALSE; - } - else if (direction == -1) - { - gtk_tree_view_collapse_row (descendants_view, path); - g_signal_stop_emission_by_name (descendants_view, "move-cursor"); - return FALSE; - } - - gtk_tree_path_free (path); - } - - return TRUE; -} - -static void -copy_tree_view_selection_cb (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer data) -{ - g_autofree gchar *name = NULL; - gchar sstr[16]; - gchar tstr[16]; - GString *str = data; - gdouble self; - gdouble total; - gint depth; - - g_assert (GTK_IS_TREE_MODEL (model)); - g_assert (path != NULL); - g_assert (iter != NULL); - g_assert (str != NULL); - - depth = gtk_tree_path_get_depth (path); - gtk_tree_model_get (model, iter, - COLUMN_NAME, &name, - COLUMN_SELF, &self, - COLUMN_TOTAL, &total, - -1); - - g_snprintf (sstr, sizeof sstr, "%.2lf%%", self); - g_snprintf (tstr, sizeof tstr, "%.2lf%%", total); - - g_string_append_printf (str, "[%8s] [%8s] ", sstr, tstr); - - for (gint i = 1; i < depth; i++) - g_string_append (str, " "); - g_string_append (str, name); - g_string_append_c (str, '\n'); -} - -static void -copy_tree_view_selection (GtkTreeView *tree_view) -{ - g_autoptr(GString) str = NULL; - GdkClipboard *clipboard; - - g_assert (GTK_IS_TREE_VIEW (tree_view)); - - str = g_string_new (" SELF TOTAL FUNCTION\n"); - gtk_tree_selection_selected_foreach (gtk_tree_view_get_selection (tree_view), - copy_tree_view_selection_cb, - str); - - clipboard = gtk_widget_get_clipboard (GTK_WIDGET (tree_view)); - gdk_clipboard_set_text (clipboard, str->str); -} - -static void -sysprof_callgraph_page_copy_cb (GtkWidget *widget, - const char *action_name, - GVariant *param) -{ - SysprofCallgraphPage *self = (SysprofCallgraphPage *)widget; - SysprofCallgraphPagePrivate *priv = sysprof_callgraph_page_get_instance_private (self); - GtkRoot *toplevel; - GtkWidget *focus; - - g_assert (GTK_IS_WIDGET (widget)); - g_assert (SYSPROF_IS_CALLGRAPH_PAGE (self)); - - if (!(toplevel = gtk_widget_get_root (widget)) || - !GTK_IS_ROOT (toplevel) || - !(focus = gtk_root_get_focus (toplevel))) - return; - - if (focus == GTK_WIDGET (priv->descendants_view)) - copy_tree_view_selection (priv->descendants_view); - else if (focus == GTK_WIDGET (priv->callers_view)) - copy_tree_view_selection (priv->callers_view); - else if (focus == GTK_WIDGET (priv->functions_view)) - copy_tree_view_selection (priv->functions_view); -} - -static void -sysprof_callgraph_page_generate_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - SysprofProfile *profile = (SysprofProfile *)object; - SysprofCallgraphPage *self; - g_autoptr(GTask) task = user_data; - g_autoptr(GError) error = NULL; - - g_assert (SYSPROF_IS_PROFILE (profile)); - g_assert (G_IS_ASYNC_RESULT (result)); - g_assert (G_IS_TASK (task)); - - self = g_task_get_source_object (task); - - if (!sysprof_profile_generate_finish (profile, result, &error)) - g_task_return_error (task, g_steal_pointer (&error)); - else - sysprof_callgraph_page_set_profile (self, SYSPROF_CALLGRAPH_PROFILE (profile)); -} - -static void -sysprof_callgraph_page_load_async (SysprofPage *page, - SysprofCaptureReader *reader, - SysprofSelection *selection, - SysprofCaptureCondition *filter, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - SysprofCallgraphPage *self = (SysprofCallgraphPage *)page; - g_autoptr(SysprofCaptureReader) copy = NULL; - g_autoptr(SysprofProfile) profile = NULL; - g_autoptr(GTask) task = NULL; - - g_assert (SYSPROF_IS_CALLGRAPH_PAGE (self)); - g_assert (reader != NULL); - g_assert (SYSPROF_IS_SELECTION (selection)); - g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); - - task = g_task_new (self, cancellable, callback, user_data); - g_task_set_source_tag (task, sysprof_callgraph_page_load_async); - - copy = sysprof_capture_reader_copy (reader); - - profile = sysprof_callgraph_profile_new_with_selection (selection); - sysprof_profile_set_reader (profile, reader); - sysprof_profile_generate (profile, - cancellable, - sysprof_callgraph_page_generate_cb, - g_steal_pointer (&task)); -} - -static gboolean -sysprof_callgraph_page_load_finish (SysprofPage *page, - GAsyncResult *result, - GError **error) -{ - g_return_val_if_fail (SYSPROF_IS_CALLGRAPH_PAGE (page), FALSE); - g_return_val_if_fail (G_IS_TASK (result), FALSE); - - return g_task_propagate_boolean (G_TASK (result), error); -} - -static void -sysprof_callgraph_page_finalize (GObject *object) -{ - SysprofCallgraphPage *self = (SysprofCallgraphPage *)object; - SysprofCallgraphPagePrivate *priv = sysprof_callgraph_page_get_instance_private (self); - - g_clear_pointer (&priv->history, g_queue_free); - g_clear_object (&priv->profile); - - G_OBJECT_CLASS (sysprof_callgraph_page_parent_class)->finalize (object); -} - -static void -sysprof_callgraph_page_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - SysprofCallgraphPage *self = SYSPROF_CALLGRAPH_PAGE (object); - SysprofCallgraphPagePrivate *priv = sysprof_callgraph_page_get_instance_private (self); - - switch (prop_id) - { - case PROP_PROFILE: - g_value_set_object (value, priv->profile); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_callgraph_page_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - SysprofCallgraphPage *self = SYSPROF_CALLGRAPH_PAGE (object); - - switch (prop_id) - { - case PROP_PROFILE: - sysprof_callgraph_page_set_profile (self, g_value_get_object (value)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_callgraph_page_class_init (SysprofCallgraphPageClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); - SysprofPageClass *page_class = SYSPROF_PAGE_CLASS (klass); - - object_class->finalize = sysprof_callgraph_page_finalize; - object_class->get_property = sysprof_callgraph_page_get_property; - object_class->set_property = sysprof_callgraph_page_set_property; - - page_class->load_async = sysprof_callgraph_page_load_async; - page_class->load_finish = sysprof_callgraph_page_load_finish; - - klass->go_previous = sysprof_callgraph_page_real_go_previous; - - properties [PROP_PROFILE] = - g_param_spec_object ("profile", - "Profile", - "The callgraph profile to view", - SYSPROF_TYPE_CALLGRAPH_PROFILE, - (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_properties (object_class, N_PROPS, properties); - - signals [GO_PREVIOUS] = - g_signal_new ("go-previous", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_STRUCT_OFFSET (SysprofCallgraphPageClass, go_previous), - NULL, NULL, NULL, G_TYPE_NONE, 0); - - gtk_widget_class_set_template_from_resource (widget_class, - "/org/gnome/sysprof/ui/sysprof-callgraph-page.ui"); - - gtk_widget_class_bind_template_child_private (widget_class, SysprofCallgraphPage, callers_view); - gtk_widget_class_bind_template_child_private (widget_class, SysprofCallgraphPage, functions_view); - gtk_widget_class_bind_template_child_private (widget_class, SysprofCallgraphPage, descendants_view); - gtk_widget_class_bind_template_child_private (widget_class, SysprofCallgraphPage, descendants_name_column); - gtk_widget_class_bind_template_child_private (widget_class, SysprofCallgraphPage, stack); - gtk_widget_class_bind_template_child_private (widget_class, SysprofCallgraphPage, callgraph); - gtk_widget_class_bind_template_child_private (widget_class, SysprofCallgraphPage, empty_state); - gtk_widget_class_bind_template_child_private (widget_class, SysprofCallgraphPage, loading_state); - - gtk_widget_class_install_action (widget_class, "page.copy", NULL, sysprof_callgraph_page_copy_cb); - - gtk_widget_class_add_binding_action (widget_class, GDK_KEY_c, GDK_CONTROL_MASK, "page.copy", NULL); - gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_Left, GDK_ALT_MASK, "go-previous", NULL); - - g_type_ensure (EGG_TYPE_PANED); - g_type_ensure (SYSPROF_TYPE_CELL_RENDERER_PERCENT); -} - -static void -sysprof_callgraph_page_init (SysprofCallgraphPage *self) -{ - SysprofCallgraphPagePrivate *priv = sysprof_callgraph_page_get_instance_private (self); - GtkTreeSelection *selection; - GtkCellRenderer *cell; - - priv->history = g_queue_new (); - - gtk_widget_init_template (GTK_WIDGET (self)); - - gtk_stack_set_visible_child (priv->stack, priv->loading_state); - - selection = gtk_tree_view_get_selection (priv->functions_view); - - g_signal_connect_object (selection, - "changed", - G_CALLBACK (sysprof_callgraph_page_function_selection_changed), - self, - G_CONNECT_SWAPPED); - - g_signal_connect_object (priv->descendants_view, - "row-activated", - G_CALLBACK (sysprof_callgraph_page_descendant_activated), - self, - G_CONNECT_SWAPPED); - - g_signal_connect_object (priv->callers_view, - "row-activated", - G_CALLBACK (sysprof_callgraph_page_caller_activated), - self, - G_CONNECT_SWAPPED); - - g_signal_connect (priv->descendants_view, - "move-cursor", - G_CALLBACK (descendants_view_move_cursor_cb), - NULL); - - cell = g_object_new (GTK_TYPE_CELL_RENDERER_TEXT, - "ellipsize", PANGO_ELLIPSIZE_MIDDLE, - "xalign", 0.0f, - NULL); - gtk_tree_view_column_pack_start (priv->descendants_name_column, cell, TRUE); - gtk_tree_view_column_add_attribute (priv->descendants_name_column, cell, "text", 0); - - cell = g_object_new (GTK_TYPE_CELL_RENDERER_TEXT, - "foreground", "#666666", - "scale", PANGO_SCALE_SMALL, - "xalign", 1.0f, - NULL); - gtk_tree_view_column_pack_start (priv->descendants_name_column, cell, FALSE); - gtk_tree_view_column_set_cell_data_func (priv->descendants_name_column, cell, - sysprof_callgraph_page_tag_data_func, - self, NULL); - - gtk_tree_selection_set_mode (gtk_tree_view_get_selection (priv->descendants_view), - GTK_SELECTION_MULTIPLE); -} - -typedef struct _Descendant Descendant; - -struct _Descendant -{ - const gchar *name; - guint self; - guint cumulative; - Descendant *parent; - Descendant *siblings; - Descendant *children; -}; - -static void -build_tree_cb (StackLink *trace, - gint size, - gpointer user_data) -{ - Descendant **tree = user_data; - Descendant *parent = NULL; - StackLink *link; - - g_assert (trace != NULL); - g_assert (tree != NULL); - - /* Get last item */ - link = trace; - while (link->next) - link = link->next; - - for (; link != NULL; link = link->prev) - { - const gchar *address = U64_TO_POINTER (link->data); - Descendant *prev = NULL; - Descendant *match = NULL; - - for (match = *tree; match != NULL; match = match->siblings) - { - if (match->name == address) - { - if (prev != NULL) - { - /* Move to front */ - prev->siblings = match->siblings; - match->siblings = *tree; - *tree = match; - } - break; - } - } - - if (match == NULL) - { - /* Have we seen this object further up the tree? */ - for (match = parent; match != NULL; match = match->parent) - { - if (match->name == address) - break; - } - } - - if (match == NULL) - { - match = g_slice_new (Descendant); - match->name = address; - match->cumulative = 0; - match->self = 0; - match->children = NULL; - match->parent = parent; - match->siblings = *tree; - *tree = match; - } - - tree = &match->children; - parent = match; - } - - parent->self += size; - - for (; parent != NULL; parent = parent->parent) - parent->cumulative += size; -} - -static Descendant * -build_tree (StackNode *node) -{ - Descendant *tree = NULL; - - for (; node != NULL; node = node->next) - { - if (node->toplevel) - stack_node_foreach_trace (node, build_tree_cb, &tree); - } - - return tree; -} - -static void -append_to_tree_and_free (SysprofCallgraphPage *self, - StackStash *stash, - GtkTreeStore *store, - Descendant *item, - GtkTreeIter *parent) -{ - StackNode *node = NULL; - GtkTreeIter iter; - guint profile_size; - - g_assert (GTK_IS_TREE_STORE (store)); - g_assert (item != NULL); - - profile_size = MAX (1, sysprof_callgraph_page_get_profile_size (self)); - - gtk_tree_store_append (store, &iter, parent); - - node = stack_stash_find_node (stash, (gpointer)item->name); - - gtk_tree_store_set (store, &iter, - COLUMN_NAME, item->name, - COLUMN_SELF, item->self * 100.0 / (gdouble)profile_size, - COLUMN_TOTAL, item->cumulative * 100.0 / (gdouble)profile_size, - COLUMN_POINTER, node, - COLUMN_HITS, (guint)item->cumulative, - -1); - - if (item->siblings != NULL) - append_to_tree_and_free (self, stash, store, item->siblings, parent); - - if (item->children != NULL) - append_to_tree_and_free (self, stash, store, item->children, &iter); - - g_slice_free (Descendant, item); -} - -static void -sysprof_callgraph_page_update_descendants (SysprofCallgraphPage *self, - StackNode *node) -{ - SysprofCallgraphPagePrivate *priv = sysprof_callgraph_page_get_instance_private (self); - GtkTreeStore *store; - - g_assert (SYSPROF_IS_CALLGRAPH_PAGE (self)); - - if (g_queue_peek_head (priv->history) != node) - g_queue_push_head (priv->history, node); - - store = gtk_tree_store_new (5, - G_TYPE_STRING, - G_TYPE_DOUBLE, - G_TYPE_DOUBLE, - G_TYPE_POINTER, - G_TYPE_UINT); - - if (priv->profile != NULL) - { - StackStash *stash; - - stash = sysprof_callgraph_profile_get_stash (priv->profile); - if (stash != NULL) - { - Descendant *tree; - - tree = build_tree (node); - if (tree != NULL) - append_to_tree_and_free (self, stash, store, tree, NULL); - } - } - - gtk_tree_view_set_model (priv->descendants_view, GTK_TREE_MODEL (store)); - gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store), - COLUMN_TOTAL, GTK_SORT_DESCENDING); - sysprof_callgraph_page_expand_descendants (self); - - g_clear_object (&store); -} - -/** - * sysprof_callgraph_page_screenshot: - * @self: A #SysprofCallgraphPage. - * - * This function will generate a text representation of the descendants tree. - * This is useful if you want to include various profiling information in a - * commit message or email. - * - * The text generated will match the current row expansion in the tree view. - * - * Returns: (nullable) (transfer full): A newly allocated string that should be freed - * with g_free(). - */ -gchar * -sysprof_callgraph_page_screenshot (SysprofCallgraphPage *self) -{ - SysprofCallgraphPagePrivate *priv = sysprof_callgraph_page_get_instance_private (self); - GtkTreeView *tree_view; - GtkTreeModel *model; - GtkTreePath *tree_path; - GString *str; - GtkTreeIter iter; - - g_return_val_if_fail (SYSPROF_IS_CALLGRAPH_PAGE (self), NULL); - - tree_view = priv->descendants_view; - - if (NULL == (model = gtk_tree_view_get_model (tree_view))) - return NULL; - - /* - * To avoid having to precalculate the deepest visible row, we - * put the timing information at the beginning of the line. - */ - - str = g_string_new (" SELF CUMULATIVE FUNCTION\n"); - tree_path = gtk_tree_path_new_first (); - - for (;;) - { - if (gtk_tree_model_get_iter (model, &iter, tree_path)) - { - guint depth = gtk_tree_path_get_depth (tree_path); - StackNode *node; - gdouble in_self; - gdouble total; - guint i; - - gtk_tree_model_get (model, &iter, - COLUMN_SELF, &in_self, - COLUMN_TOTAL, &total, - COLUMN_POINTER, &node, - -1); - - g_string_append_printf (str, "[% 7.2lf%%] [% 7.2lf%%] ", in_self, total); - - for (i = 0; i < depth; i++) - g_string_append (str, " "); - g_string_append (str, GSIZE_TO_POINTER (node->data)); - g_string_append_c (str, '\n'); - - if (gtk_tree_view_row_expanded (tree_view, tree_path)) - gtk_tree_path_down (tree_path); - else - gtk_tree_path_next (tree_path); - - continue; - } - - if (!gtk_tree_path_up (tree_path) || !gtk_tree_path_get_depth (tree_path)) - break; - - gtk_tree_path_next (tree_path); - } - - gtk_tree_path_free (tree_path); - - return g_string_free (str, FALSE); -} - -guint -sysprof_callgraph_page_get_n_functions (SysprofCallgraphPage *self) -{ - SysprofCallgraphPagePrivate *priv = sysprof_callgraph_page_get_instance_private (self); - GtkTreeModel *model; - guint ret = 0; - - g_return_val_if_fail (SYSPROF_IS_CALLGRAPH_PAGE (self), 0); - - if (NULL != (model = gtk_tree_view_get_model (priv->functions_view))) - ret = gtk_tree_model_iter_n_children (model, NULL); - - return ret; -} - -void -_sysprof_callgraph_page_set_loading (SysprofCallgraphPage *self, - gboolean loading) -{ - SysprofCallgraphPagePrivate *priv = sysprof_callgraph_page_get_instance_private (self); - - g_return_if_fail (SYSPROF_IS_CALLGRAPH_PAGE (self)); - - if (loading) - priv->loading++; - else - priv->loading--; - - if (priv->loading) - gtk_stack_set_visible_child (priv->stack, priv->loading_state); - else - gtk_stack_set_visible_child (priv->stack, priv->callgraph); -} diff --git a/src/libsysprof-ui/sysprof-callgraph-page.h b/src/libsysprof-ui/sysprof-callgraph-page.h deleted file mode 100644 index 7624c580..00000000 --- a/src/libsysprof-ui/sysprof-callgraph-page.h +++ /dev/null @@ -1,51 +0,0 @@ -/* sysprof-callgraph-page.h - * - * Copyright 2016-2019 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 - -#include "sysprof-page.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_CALLGRAPH_PAGE (sysprof_callgraph_page_get_type()) - -G_DECLARE_DERIVABLE_TYPE (SysprofCallgraphPage, sysprof_callgraph_page, SYSPROF, CALLGRAPH_PAGE, SysprofPage) - -struct _SysprofCallgraphPageClass -{ - SysprofPageClass parent_class; - - void (*go_previous) (SysprofCallgraphPage *self); - - /*< private >*/ - gpointer _reserved[16]; -}; - -GtkWidget *sysprof_callgraph_page_new (void); -SysprofCallgraphProfile *sysprof_callgraph_page_get_profile (SysprofCallgraphPage *self); -void sysprof_callgraph_page_set_profile (SysprofCallgraphPage *self, - SysprofCallgraphProfile *profile); -gchar *sysprof_callgraph_page_screenshot (SysprofCallgraphPage *self); -guint sysprof_callgraph_page_get_n_functions (SysprofCallgraphPage *self); - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-callgraph-page.ui b/src/libsysprof-ui/sysprof-callgraph-page.ui deleted file mode 100644 index b0dc4f26..00000000 --- a/src/libsysprof-ui/sysprof-callgraph-page.ui +++ /dev/null @@ -1,212 +0,0 @@ - - - diff --git a/src/libsysprof-ui/sysprof-cell-renderer-duration.c b/src/libsysprof-ui/sysprof-cell-renderer-duration.c deleted file mode 100644 index 3e60166e..00000000 --- a/src/libsysprof-ui/sysprof-cell-renderer-duration.c +++ /dev/null @@ -1,455 +0,0 @@ -/* sysprof-cell-renderer-duration.c - * - * Copyright 2019 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 - */ - -#define G_LOG_DOMAIN "sysprof-cell-renderer-duration" - -#include "config.h" - -#include "sysprof-cell-renderer-duration.h" -#include "sysprof-ui-private.h" -#include "sysprof-zoom-manager.h" - -typedef struct -{ - gint64 capture_begin_time; - gint64 capture_end_time; - gint64 capture_duration; - gint64 begin_time; - gint64 end_time; - gchar *text; - SysprofZoomManager *zoom_manager; - GdkRGBA color; - guint color_set : 1; -} SysprofCellRendererDurationPrivate; - -enum { - PROP_0, - PROP_BEGIN_TIME, - PROP_CAPTURE_BEGIN_TIME, - PROP_CAPTURE_END_TIME, - PROP_COLOR, - PROP_END_TIME, - PROP_TEXT, - PROP_ZOOM_MANAGER, - N_PROPS -}; - -G_DEFINE_TYPE_WITH_PRIVATE (SysprofCellRendererDuration, sysprof_cell_renderer_duration, GTK_TYPE_CELL_RENDERER) - -static GParamSpec *properties [N_PROPS]; - -static inline void -rounded_rectangle (cairo_t *cr, - const GdkRectangle *rect, - int x_radius, - int y_radius) -{ - int x; - int y; - int width; - int height; - int x1, x2; - int y1, y2; - int xr1, xr2; - int yr1, yr2; - - g_assert (cr); - g_assert (rect); - - x = rect->x; - y = rect->y; - width = rect->width; - height = rect->height; - - x1 = x; - x2 = x1 + width; - y1 = y; - y2 = y1 + height; - - x_radius = MIN (x_radius, width / 2.0); - y_radius = MIN (y_radius, width / 2.0); - - xr1 = x_radius; - xr2 = x_radius / 2.0; - yr1 = y_radius; - yr2 = y_radius / 2.0; - - cairo_move_to (cr, x1 + xr1, y1); - cairo_line_to (cr, x2 - xr1, y1); - cairo_curve_to (cr, x2 - xr2, y1, x2, y1 + yr2, x2, y1 + yr1); - cairo_line_to (cr, x2, y2 - yr1); - cairo_curve_to (cr, x2, y2 - yr2, x2 - xr2, y2, x2 - xr1, y2); - cairo_line_to (cr, x1 + xr1, y2); - cairo_curve_to (cr, x1 + xr2, y2, x1, y2 - yr2, x1, y2 - yr1); - cairo_line_to (cr, x1, y1 + yr1); - cairo_curve_to (cr, x1, y1 + yr2, x1 + xr2, y1, x1 + xr1, y1); - cairo_close_path (cr); -} - -static void -sysprof_cell_renderer_duration_snapshot (GtkCellRenderer *renderer, - GtkSnapshot *snapshot, - GtkWidget *widget, - const GdkRectangle *bg_area, - const GdkRectangle *cell_area, - GtkCellRendererState state) -{ - SysprofCellRendererDuration *self = (SysprofCellRendererDuration *)renderer; - SysprofCellRendererDurationPrivate *priv = sysprof_cell_renderer_duration_get_instance_private (self); - g_autoptr(GString) str = NULL; - GtkStyleContext *style_context; - cairo_t *cr; - gdouble x1, x2; - GdkRGBA rgba; - GdkRectangle r; - gint64 duration; - - g_assert (SYSPROF_IS_CELL_RENDERER_DURATION (self)); - g_assert (snapshot != NULL); - g_assert (GTK_IS_WIDGET (widget)); - - if (priv->zoom_manager == NULL) - return; - - cr = gtk_snapshot_append_cairo (snapshot, &GRAPHENE_RECT_INIT (cell_area->x, cell_area->y, cell_area->width, cell_area->height)); - - style_context = gtk_widget_get_style_context (widget); - - if (priv->color_set) - rgba = priv->color; - else - gtk_style_context_get_color (style_context, &rgba); - - duration = sysprof_zoom_manager_get_duration_for_width (priv->zoom_manager, bg_area->width); - - x1 = (priv->begin_time - priv->capture_begin_time) / (gdouble)duration * cell_area->width; - x2 = (priv->end_time - priv->capture_begin_time) / (gdouble)duration * cell_area->width; - - if (x2 < x1) - x2 = x1; - - r.x = cell_area->x + x1; - r.height = 12; - r.y = cell_area->y + (cell_area->height - r.height) / 2; - r.width = MAX (1.0, x2 - x1); - - if ((cell_area->height - r.height) % 2 == 1) - r.height++; - - gdk_cairo_set_source_rgba (cr, &rgba); - - if (r.width > 3) - { - rounded_rectangle (cr, &r, 2, 2); - cairo_fill (cr); - } - else if (r.width > 1) - { - gdk_cairo_rectangle (cr, &r); - cairo_fill (cr); - } - else - { - cairo_set_line_width (cr, 1); - cairo_move_to (cr, r.x + .5, r.y); - cairo_line_to (cr, r.x + .5, r.y + r.height); - cairo_stroke (cr); - } - - str = g_string_new (NULL); - - if (priv->begin_time != priv->end_time) - { - g_autofree gchar *fmt = _sysprof_format_duration (priv->end_time - priv->begin_time); - g_string_append_printf (str, "%s — ", fmt); - } - - if (priv->text != NULL) - g_string_append (str, priv->text); - - if (str->len) - { - PangoLayout *layout; - gint w, h; - - /* Add some spacing before/after */ - r.x -= 24; - r.width += 48; - - layout = gtk_widget_create_pango_layout (widget, NULL); - pango_layout_set_text (layout, str->str, str->len); - pango_layout_get_pixel_size (layout, &w, &h); - - if ((r.x + r.width + w) < (cell_area->x + cell_area->width) || - ((cell_area->x + w) > r.x)) - cairo_move_to (cr, r.x + r.width, r.y + ((r.height - h) / 2)); - else - cairo_move_to (cr, r.x - w, r.y + ((r.height - h) / 2)); - - if (priv->end_time < priv->begin_time) - { - gdk_rgba_parse (&rgba, "#f00"); - if (state & GTK_CELL_RENDERER_SELECTED) - rgba.alpha = 0.6; - } - - gdk_cairo_set_source_rgba (cr, &rgba); - pango_cairo_show_layout (cr, layout); - - g_object_unref (layout); - } - - cairo_destroy (cr); -} - -static GtkSizeRequestMode -sysprof_cell_renderer_duration_get_request_mode (GtkCellRenderer *renderer) -{ - return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH; -} - -static void -sysprof_cell_renderer_duration_get_preferred_width (GtkCellRenderer *cell, - GtkWidget *widget, - gint *min_width, - gint *nat_width) -{ - SysprofCellRendererDuration *self = (SysprofCellRendererDuration *)cell; - SysprofCellRendererDurationPrivate *priv = sysprof_cell_renderer_duration_get_instance_private (self); - gint width = 1; - - g_assert (SYSPROF_IS_CELL_RENDERER_DURATION (self)); - g_assert (GTK_IS_WIDGET (widget)); - - GTK_CELL_RENDERER_CLASS (sysprof_cell_renderer_duration_parent_class)->get_preferred_width (cell, widget, min_width, nat_width); - - if (priv->zoom_manager && priv->capture_begin_time && priv->capture_end_time) - width = sysprof_zoom_manager_get_width_for_duration (priv->zoom_manager, - priv->capture_end_time - priv->capture_begin_time); - - if (min_width) - *min_width = width; - - if (nat_width) - *nat_width = width; -} - -static void -sysprof_cell_renderer_duration_get_preferred_height_for_width (GtkCellRenderer *cell, - GtkWidget *widget, - gint width, - gint *min_height, - gint *nat_height) -{ - PangoLayout *layout; - gint w, h; - gint ypad; - - g_assert (SYSPROF_IS_CELL_RENDERER_DURATION (cell)); - - gtk_cell_renderer_get_padding (cell, NULL, &ypad); - - layout = gtk_widget_create_pango_layout (widget, "XMZ09"); - pango_layout_get_pixel_size (layout, &w, &h); - g_clear_object (&layout); - - if (min_height) - *min_height = h + (ypad * 2); - - if (nat_height) - *nat_height = h + (ypad * 2); -} - -static void -sysprof_cell_renderer_duration_finalize (GObject *object) -{ - SysprofCellRendererDuration *self = (SysprofCellRendererDuration *)object; - SysprofCellRendererDurationPrivate *priv = sysprof_cell_renderer_duration_get_instance_private (self); - - g_clear_object (&priv->zoom_manager); - g_clear_pointer (&priv->text, g_free); - - G_OBJECT_CLASS (sysprof_cell_renderer_duration_parent_class)->finalize (object); -} - -static void -sysprof_cell_renderer_duration_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - SysprofCellRendererDuration *self = SYSPROF_CELL_RENDERER_DURATION (object); - SysprofCellRendererDurationPrivate *priv = sysprof_cell_renderer_duration_get_instance_private (self); - - switch (prop_id) - { - case PROP_BEGIN_TIME: - g_value_set_int64 (value, priv->begin_time); - break; - - case PROP_CAPTURE_BEGIN_TIME: - g_value_set_int64 (value, priv->capture_begin_time); - break; - - case PROP_CAPTURE_END_TIME: - g_value_set_int64 (value, priv->capture_end_time); - break; - - case PROP_END_TIME: - g_value_set_int64 (value, priv->end_time); - break; - - case PROP_TEXT: - g_value_set_string (value, priv->text); - break; - - case PROP_ZOOM_MANAGER: - g_value_set_object (value, priv->zoom_manager); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_cell_renderer_duration_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - SysprofCellRendererDuration *self = SYSPROF_CELL_RENDERER_DURATION (object); - SysprofCellRendererDurationPrivate *priv = sysprof_cell_renderer_duration_get_instance_private (self); - - switch (prop_id) - { - case PROP_BEGIN_TIME: - priv->begin_time = g_value_get_int64 (value); - break; - - case PROP_CAPTURE_BEGIN_TIME: - priv->capture_begin_time = g_value_get_int64 (value); - priv->capture_duration = priv->capture_end_time - priv->capture_begin_time; - break; - - case PROP_CAPTURE_END_TIME: - priv->capture_end_time = g_value_get_int64 (value); - priv->capture_duration = priv->capture_end_time - priv->capture_begin_time; - break; - - case PROP_COLOR: - if (g_value_get_boxed (value)) - priv->color = *(GdkRGBA *)g_value_get_boxed (value); - else - gdk_rgba_parse (&priv->color, "#000"); - priv->color_set = !!g_value_get_boolean (value); - break; - - case PROP_END_TIME: - priv->end_time = g_value_get_int64 (value); - break; - - case PROP_TEXT: - g_free (priv->text); - priv->text = g_value_dup_string (value); - break; - - case PROP_ZOOM_MANAGER: - g_set_object (&priv->zoom_manager, g_value_get_object (value)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_cell_renderer_duration_class_init (SysprofCellRendererDurationClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (klass); - - object_class->finalize = sysprof_cell_renderer_duration_finalize; - object_class->get_property = sysprof_cell_renderer_duration_get_property; - object_class->set_property = sysprof_cell_renderer_duration_set_property; - - cell_class->get_preferred_height_for_width = sysprof_cell_renderer_duration_get_preferred_height_for_width; - cell_class->get_preferred_width = sysprof_cell_renderer_duration_get_preferred_width; - cell_class->get_request_mode = sysprof_cell_renderer_duration_get_request_mode; - cell_class->snapshot = sysprof_cell_renderer_duration_snapshot; - - /* Note we do not emit ::notify() for these properties */ - - properties [PROP_BEGIN_TIME] = - g_param_spec_int64 ("begin-time", NULL, NULL, - G_MININT64, G_MAXINT64, 0, - (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); - - properties [PROP_CAPTURE_BEGIN_TIME] = - g_param_spec_int64 ("capture-begin-time", NULL, NULL, - G_MININT64, G_MAXINT64, 0, - (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); - - properties [PROP_CAPTURE_END_TIME] = - g_param_spec_int64 ("capture-end-time", NULL, NULL, - G_MININT64, G_MAXINT64, 0, - (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); - - properties [PROP_COLOR] = - g_param_spec_boxed ("color", NULL, NULL, - GDK_TYPE_RGBA, - (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); - - properties [PROP_END_TIME] = - g_param_spec_int64 ("end-time", NULL, NULL, - G_MININT64, G_MAXINT64, 0, - (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); - - properties [PROP_END_TIME] = - g_param_spec_int64 ("end-time", NULL, NULL, - G_MININT64, G_MAXINT64, 0, - (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); - - properties [PROP_TEXT] = - g_param_spec_string ("text", NULL, NULL, - NULL, - (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); - - properties [PROP_ZOOM_MANAGER] = - g_param_spec_object ("zoom-manager", NULL, NULL, - SYSPROF_TYPE_ZOOM_MANAGER, - (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_properties (object_class, N_PROPS, properties); -} - -static void -sysprof_cell_renderer_duration_init (SysprofCellRendererDuration *self) -{ - SysprofCellRendererDurationPrivate *priv = sysprof_cell_renderer_duration_get_instance_private (self); - - priv->color.alpha = 1.0; -} - -GtkCellRenderer * -sysprof_cell_renderer_duration_new (void) -{ - return g_object_new (SYSPROF_TYPE_CELL_RENDERER_DURATION, NULL); -} diff --git a/src/libsysprof-ui/sysprof-cell-renderer-duration.h b/src/libsysprof-ui/sysprof-cell-renderer-duration.h deleted file mode 100644 index 243a261c..00000000 --- a/src/libsysprof-ui/sysprof-cell-renderer-duration.h +++ /dev/null @@ -1,41 +0,0 @@ -/* sysprof-cell-renderer-duration.h - * - * Copyright 2019 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_CELL_RENDERER_DURATION (sysprof_cell_renderer_duration_get_type()) - -G_DECLARE_DERIVABLE_TYPE (SysprofCellRendererDuration, sysprof_cell_renderer_duration, SYSPROF, CELL_RENDERER_DURATION, GtkCellRenderer) - -struct _SysprofCellRendererDurationClass -{ - GtkCellRendererClass parent_class; - - /*< private >*/ - gpointer _reserved[8]; -}; - -GtkCellRenderer *sysprof_cell_renderer_duration_new (void); - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-cell-renderer-percent.c b/src/libsysprof-ui/sysprof-cell-renderer-percent.c deleted file mode 100644 index e23e1fed..00000000 --- a/src/libsysprof-ui/sysprof-cell-renderer-percent.c +++ /dev/null @@ -1,139 +0,0 @@ -/* sysprof-cell-renderer-percent.c - * - * Copyright 2016-2019 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 - */ - -#define G_LOG_DOMAIN "sysprof-cell-renderer-percent" - -#include "config.h" - -#include - -#include "sysprof-cell-renderer-percent.h" - -typedef struct -{ - gdouble percent; -} SysprofCellRendererPercentPrivate; - -enum { - PROP_0, - PROP_PERCENT, - N_PROPS -}; - -G_DEFINE_TYPE_WITH_PRIVATE (SysprofCellRendererPercent, sysprof_cell_renderer_percent, SYSPROF_TYPE_CELL_RENDERER_PROGRESS) - -static GParamSpec *properties [N_PROPS]; - -static void -sysprof_cell_renderer_percent_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - SysprofCellRendererPercent *self = SYSPROF_CELL_RENDERER_PERCENT (object); - - switch (prop_id) - { - case PROP_PERCENT: - g_value_set_double (value, sysprof_cell_renderer_percent_get_percent (self)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_cell_renderer_percent_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - SysprofCellRendererPercent *self = SYSPROF_CELL_RENDERER_PERCENT (object); - - switch (prop_id) - { - case PROP_PERCENT: - sysprof_cell_renderer_percent_set_percent (self, g_value_get_double (value)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_cell_renderer_percent_class_init (SysprofCellRendererPercentClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->get_property = sysprof_cell_renderer_percent_get_property; - object_class->set_property = sysprof_cell_renderer_percent_set_property; - - properties [PROP_PERCENT] = - g_param_spec_double ("percent", - "Percent", - "Percent", - 0.0, - 100.0, - 0.0, - /* Doesn't notify to avoid signal emission */ - (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_properties (object_class, N_PROPS, properties); -} - -static void -sysprof_cell_renderer_percent_init (SysprofCellRendererPercent *self) -{ - g_object_set (self, "text-xalign", 1.0f, NULL); -} - -gdouble -sysprof_cell_renderer_percent_get_percent (SysprofCellRendererPercent *self) -{ - SysprofCellRendererPercentPrivate *priv = sysprof_cell_renderer_percent_get_instance_private (self); - - g_return_val_if_fail (SYSPROF_IS_CELL_RENDERER_PERCENT (self), 0.0); - - return priv->percent; -} - -void -sysprof_cell_renderer_percent_set_percent (SysprofCellRendererPercent *self, - gdouble percent) -{ - SysprofCellRendererPercentPrivate *priv = sysprof_cell_renderer_percent_get_instance_private (self); - gchar text[8]; - - g_return_if_fail (SYSPROF_IS_CELL_RENDERER_PERCENT (self)); - g_return_if_fail (percent >= 0.0); - g_return_if_fail (percent <= 100.0); - - priv->percent = percent; - - g_snprintf (text, sizeof text, "%.2lf%%", percent); - text [sizeof text - 1] = '\0'; - - g_object_set (self, - "value", (guint)percent, - "text", text, - NULL); -} diff --git a/src/libsysprof-ui/sysprof-cell-renderer-percent.h b/src/libsysprof-ui/sysprof-cell-renderer-percent.h deleted file mode 100644 index 44a8fc04..00000000 --- a/src/libsysprof-ui/sysprof-cell-renderer-percent.h +++ /dev/null @@ -1,57 +0,0 @@ -/* sysprof-cell-renderer-percent.h - * - * Copyright 2016-2019 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-cell-renderer-progress.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_CELL_RENDERER_PERCENT (sysprof_cell_renderer_percent_get_type()) -#define SYSPROF_CELL_RENDERER_PERCENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SYSPROF_TYPE_CELL_RENDERER_PERCENT, SysprofCellRendererPercent)) -#define SYSPROF_CELL_RENDERER_PERCENT_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SYSPROF_TYPE_CELL_RENDERER_PERCENT, SysprofCellRendererPercent const)) -#define SYSPROF_CELL_RENDERER_PERCENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SYSPROF_TYPE_CELL_RENDERER_PERCENT, SysprofCellRendererPercentClass)) -#define SYSPROF_IS_CELL_RENDERER_PERCENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SYSPROF_TYPE_CELL_RENDERER_PERCENT)) -#define SYSPROF_IS_CELL_RENDERER_PERCENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SYSPROF_TYPE_CELL_RENDERER_PERCENT)) -#define SYSPROF_CELL_RENDERER_PERCENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SYSPROF_TYPE_CELL_RENDERER_PERCENT, SysprofCellRendererPercentClass)) - -typedef struct _SysprofCellRendererPercent SysprofCellRendererPercent; -typedef struct _SysprofCellRendererPercentClass SysprofCellRendererPercentClass; - -struct _SysprofCellRendererPercent -{ - SysprofCellRendererProgress parent; -}; - -struct _SysprofCellRendererPercentClass -{ - SysprofCellRendererProgressClass parent_class; - - /*< private >*/ - gpointer _reserved[4]; -}; - -GType sysprof_cell_renderer_percent_get_type (void); -GtkCellRenderer *sysprof_cell_renderer_percent_new (void); -gdouble sysprof_cell_renderer_percent_get_percent (SysprofCellRendererPercent *self); -void sysprof_cell_renderer_percent_set_percent (SysprofCellRendererPercent *self, - gdouble percent); - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-cell-renderer-progress.c b/src/libsysprof-ui/sysprof-cell-renderer-progress.c deleted file mode 100644 index a0943b64..00000000 --- a/src/libsysprof-ui/sysprof-cell-renderer-progress.c +++ /dev/null @@ -1,712 +0,0 @@ -/* gtkcellrendererprogress.c - * Copyright (C) 2002 Naba Kumar - * heavily modified by Jörgen Scheibengruber - * heavily modified by Marco Pesenti Gritti - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library. If not, see . - */ -/* - * Modified by the GTK+ Team and others 1997-2007. See the AUTHORS - * file for a list of people on the GTK+ Team. See the ChangeLog - * files for a list of changes. These files are distributed with - * GTK+ at ftp://ftp.gtk.org/pub/gtk/. - */ - -#include "config.h" - -#include -#include - -#include "sysprof-cell-renderer-progress.h" - - -enum -{ - PROP_0, - PROP_VALUE, - PROP_TEXT, - PROP_PULSE, - PROP_TEXT_XALIGN, - PROP_TEXT_YALIGN, - PROP_ORIENTATION, - PROP_INVERTED -}; - -struct _SysprofCellRendererProgressPrivate -{ - int value; - char *text; - char *label; - int min_h; - int min_w; - int pulse; - int offset; - float text_xalign; - float text_yalign; - GtkOrientation orientation; - gboolean inverted; -}; - -static void sysprof_cell_renderer_progress_finalize (GObject *object); -static void sysprof_cell_renderer_progress_get_property (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec); -static void sysprof_cell_renderer_progress_set_property (GObject *object, - guint param_id, - const GValue *value, - GParamSpec *pspec); -static void sysprof_cell_renderer_progress_set_value (SysprofCellRendererProgress *cellprogress, - int value); -static void sysprof_cell_renderer_progress_set_text (SysprofCellRendererProgress *cellprogress, - const char *text); -static void sysprof_cell_renderer_progress_set_pulse (SysprofCellRendererProgress *cellprogress, - int pulse); -static void compute_dimensions (GtkCellRenderer *cell, - GtkWidget *widget, - const char *text, - int *width, - int *height); -static void sysprof_cell_renderer_progress_snapshot (GtkCellRenderer *cell, - GtkSnapshot *snapshot, - GtkWidget *widget, - const GdkRectangle *background_area, - const GdkRectangle *cell_area, - GtkCellRendererState flags); - - -G_DEFINE_TYPE_WITH_CODE (SysprofCellRendererProgress, sysprof_cell_renderer_progress, GTK_TYPE_CELL_RENDERER, - G_ADD_PRIVATE (SysprofCellRendererProgress) - G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL)) - -static void -recompute_label (SysprofCellRendererProgress *cellprogress) -{ - SysprofCellRendererProgressPrivate *priv = sysprof_cell_renderer_progress_get_instance_private (cellprogress); - char *label; - - if (priv->text) - label = g_strdup (priv->text); - else if (priv->pulse < 0) - label = g_strdup_printf (C_("progress bar label", "%d %%"), priv->value); - else - label = NULL; - - g_free (priv->label); - priv->label = label; -} - -static void -sysprof_cell_renderer_progress_set_value (SysprofCellRendererProgress *cellprogress, - int value) -{ - SysprofCellRendererProgressPrivate *priv = sysprof_cell_renderer_progress_get_instance_private (cellprogress); - - if (priv->value != value) - { - priv->value = value; - recompute_label (cellprogress); - g_object_notify (G_OBJECT (cellprogress), "value"); - } -} - -static void -sysprof_cell_renderer_progress_set_text (SysprofCellRendererProgress *cellprogress, - const char *text) -{ - SysprofCellRendererProgressPrivate *priv = sysprof_cell_renderer_progress_get_instance_private (cellprogress); - char *new_text; - - new_text = g_strdup (text); - g_free (priv->text); - priv->text = new_text; - recompute_label (cellprogress); - g_object_notify (G_OBJECT (cellprogress), "text"); -} - -static void -sysprof_cell_renderer_progress_set_pulse (SysprofCellRendererProgress *cellprogress, - int pulse) -{ - SysprofCellRendererProgressPrivate *priv = sysprof_cell_renderer_progress_get_instance_private (cellprogress); - - if (pulse != priv->pulse) - { - if (pulse <= 0) - priv->offset = 0; - else - priv->offset = pulse; - g_object_notify (G_OBJECT (cellprogress), "pulse"); - } - - priv->pulse = pulse; - recompute_label (cellprogress); -} - -static void -sysprof_cell_renderer_progress_finalize (GObject *object) -{ - SysprofCellRendererProgress *cellprogress = SYSPROF_CELL_RENDERER_PROGRESS (object); - SysprofCellRendererProgressPrivate *priv = sysprof_cell_renderer_progress_get_instance_private (cellprogress); - - g_free (priv->text); - g_free (priv->label); - - G_OBJECT_CLASS (sysprof_cell_renderer_progress_parent_class)->finalize (object); -} - -static void -sysprof_cell_renderer_progress_get_property (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec) -{ - SysprofCellRendererProgress *cellprogress = SYSPROF_CELL_RENDERER_PROGRESS (object); - SysprofCellRendererProgressPrivate *priv = sysprof_cell_renderer_progress_get_instance_private (cellprogress); - - switch (param_id) - { - case PROP_VALUE: - g_value_set_int (value, priv->value); - break; - case PROP_TEXT: - g_value_set_string (value, priv->text); - break; - case PROP_PULSE: - g_value_set_int (value, priv->pulse); - break; - case PROP_TEXT_XALIGN: - g_value_set_float (value, priv->text_xalign); - break; - case PROP_TEXT_YALIGN: - g_value_set_float (value, priv->text_yalign); - break; - case PROP_ORIENTATION: - g_value_set_enum (value, priv->orientation); - break; - case PROP_INVERTED: - g_value_set_boolean (value, priv->inverted); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); - } -} - -static void -sysprof_cell_renderer_progress_set_property (GObject *object, - guint param_id, - const GValue *value, - GParamSpec *pspec) -{ - SysprofCellRendererProgress *cellprogress = SYSPROF_CELL_RENDERER_PROGRESS (object); - SysprofCellRendererProgressPrivate *priv = sysprof_cell_renderer_progress_get_instance_private (cellprogress); - - switch (param_id) - { - case PROP_VALUE: - sysprof_cell_renderer_progress_set_value (cellprogress, - g_value_get_int (value)); - break; - case PROP_TEXT: - sysprof_cell_renderer_progress_set_text (cellprogress, - g_value_get_string (value)); - break; - case PROP_PULSE: - sysprof_cell_renderer_progress_set_pulse (cellprogress, - g_value_get_int (value)); - break; - case PROP_TEXT_XALIGN: - priv->text_xalign = g_value_get_float (value); - break; - case PROP_TEXT_YALIGN: - priv->text_yalign = g_value_get_float (value); - break; - case PROP_ORIENTATION: - if (priv->orientation != g_value_get_enum (value)) - { - priv->orientation = g_value_get_enum (value); - g_object_notify_by_pspec (object, pspec); - } - break; - case PROP_INVERTED: - if (priv->inverted != g_value_get_boolean (value)) - { - priv->inverted = g_value_get_boolean (value); - g_object_notify_by_pspec (object, pspec); - } - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); - } -} - -static void -compute_dimensions (GtkCellRenderer *cell, - GtkWidget *widget, - const char *text, - int *width, - int *height) -{ - PangoRectangle logical_rect; - PangoLayout *layout; - int xpad, ypad; - - layout = gtk_widget_create_pango_layout (widget, text); - pango_layout_get_pixel_extents (layout, NULL, &logical_rect); - - gtk_cell_renderer_get_padding (cell, &xpad, &ypad); - - if (width) - *width = logical_rect.width + xpad * 2; - - if (height) - *height = logical_rect.height + ypad * 2; - - g_object_unref (layout); -} - -static void -sysprof_cell_renderer_progress_get_preferred_width (GtkCellRenderer *cell, - GtkWidget *widget, - int *minimum, - int *natural) -{ - SysprofCellRendererProgress *self = SYSPROF_CELL_RENDERER_PROGRESS (cell); - SysprofCellRendererProgressPrivate *priv = sysprof_cell_renderer_progress_get_instance_private (self); - int w, h; - int size; - - if (priv->min_w < 0) - { - char *text = g_strdup_printf (C_("progress bar label", "%d %%"), 100); - compute_dimensions (cell, widget, text, - &priv->min_w, - &priv->min_h); - g_free (text); - } - - compute_dimensions (cell, widget, priv->label, &w, &h); - - size = MAX (priv->min_w, w); - - if (minimum != NULL) - *minimum = size; - if (natural != NULL) - *natural = size; -} - -static void -sysprof_cell_renderer_progress_get_preferred_height (GtkCellRenderer *cell, - GtkWidget *widget, - int *minimum, - int *natural) -{ - SysprofCellRendererProgress *self = SYSPROF_CELL_RENDERER_PROGRESS (cell); - SysprofCellRendererProgressPrivate *priv = sysprof_cell_renderer_progress_get_instance_private (self); - int w, h; - int size; - - if (priv->min_w < 0) - { - char *text = g_strdup_printf (C_("progress bar label", "%d %%"), 100); - compute_dimensions (cell, widget, text, - &priv->min_w, - &priv->min_h); - g_free (text); - } - - compute_dimensions (cell, widget, priv->label, &w, &h); - - size = MIN (priv->min_h, h); - - if (minimum != NULL) - *minimum = size; - if (natural != NULL) - *natural = size; -} - -static inline int -get_bar_size (int pulse, - int value, - int full_size) -{ - int bar_size; - - if (pulse < 0) - bar_size = full_size * MAX (0, value) / 100; - else if (pulse == 0) - bar_size = 0; - else if (pulse == G_MAXINT) - bar_size = full_size; - else - bar_size = MAX (2, full_size / 5); - - return bar_size; -} - -static inline int -get_bar_position (int start, - int full_size, - int bar_size, - int pulse, - int offset, - gboolean is_rtl) -{ - int position; - - if (pulse < 0 || pulse == 0 || pulse == G_MAXINT) - { - position = is_rtl ? (start + full_size - bar_size) : start; - } - else - { - position = (is_rtl ? offset + 12 : offset) % 24; - if (position > 12) - position = 24 - position; - position = start + full_size * position / 15; - } - - return position; -} - -static void -sysprof_cell_renderer_progress_snapshot (GtkCellRenderer *cell, - GtkSnapshot *snapshot, - GtkWidget *widget, - const GdkRectangle *background_area, - const GdkRectangle *cell_area, - GtkCellRendererState flags) -{ - SysprofCellRendererProgress *cellprogress = SYSPROF_CELL_RENDERER_PROGRESS (cell); - SysprofCellRendererProgressPrivate *priv = sysprof_cell_renderer_progress_get_instance_private (cellprogress); - GtkStyleContext *context; - GtkBorder padding; - PangoLayout *layout; - PangoRectangle logical_rect; - int x, y, w, h, x_pos, y_pos, bar_position, bar_size, start, full_size; - int xpad, ypad; - GdkRectangle clip; - gboolean is_rtl; - - context = gtk_widget_get_style_context (widget); - is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL; - - gtk_cell_renderer_get_padding (cell, &xpad, &ypad); - x = cell_area->x + xpad; - y = cell_area->y + ypad; - w = cell_area->width - xpad * 2; - h = cell_area->height - ypad * 2; - - gtk_style_context_save (context); - gtk_style_context_add_class (context, "trough"); - - gtk_snapshot_render_background (snapshot, context, x, y, w, h); - gtk_snapshot_render_frame (snapshot, context, x, y, w, h); - - gtk_style_context_get_padding (context, &padding); - - x += padding.left; - y += padding.top; - w -= padding.left + padding.right; - h -= padding.top + padding.bottom; - - gtk_style_context_restore (context); - - if (priv->orientation == GTK_ORIENTATION_HORIZONTAL) - { - clip.y = y; - clip.height = h; - - start = x; - full_size = w; - - bar_size = get_bar_size (priv->pulse, priv->value, full_size); - - if (!priv->inverted) - bar_position = get_bar_position (start, full_size, bar_size, - priv->pulse, priv->offset, is_rtl); - else - bar_position = get_bar_position (start, full_size, bar_size, - priv->pulse, priv->offset, !is_rtl); - - clip.width = bar_size; - clip.x = bar_position; - } - else - { - clip.x = x; - clip.width = w; - - start = y; - full_size = h; - - bar_size = get_bar_size (priv->pulse, priv->value, full_size); - - if (priv->inverted) - bar_position = get_bar_position (start, full_size, bar_size, - priv->pulse, priv->offset, TRUE); - else - bar_position = get_bar_position (start, full_size, bar_size, - priv->pulse, priv->offset, FALSE); - - clip.height = bar_size; - clip.y = bar_position; - } - - if (bar_size > 0) - { - gtk_style_context_save (context); - gtk_style_context_add_class (context, "progressbar"); - - gtk_snapshot_render_background (snapshot, context, clip.x, clip.y, clip.width, clip.height); - gtk_snapshot_render_frame (snapshot, context, clip.x, clip.y, clip.width, clip.height); - - gtk_style_context_restore (context); - } - - if (priv->label) - { - float text_xalign; - - layout = gtk_widget_create_pango_layout (widget, priv->label); - pango_layout_get_pixel_extents (layout, NULL, &logical_rect); - - if (gtk_widget_get_direction (widget) != GTK_TEXT_DIR_LTR) - text_xalign = 1.0 - priv->text_xalign; - else - text_xalign = priv->text_xalign; - - x_pos = x + padding.left + text_xalign * - (w - padding.left - padding.right - logical_rect.width); - - y_pos = y + padding.top + priv->text_yalign * - (h - padding.top - padding.bottom - logical_rect.height); - - gtk_snapshot_push_clip (snapshot, - &GRAPHENE_RECT_INIT( - clip.x, clip.y, - clip.width, clip.height - )); - - gtk_style_context_save (context); - gtk_style_context_add_class (context, "progressbar"); - - gtk_snapshot_render_layout (snapshot, context, - x_pos, y_pos, - layout); - - gtk_style_context_restore (context); - gtk_snapshot_pop (snapshot); - - gtk_style_context_save (context); - gtk_style_context_add_class (context, "trough"); - - if (bar_position > start) - { - if (priv->orientation == GTK_ORIENTATION_HORIZONTAL) - { - clip.x = x; - clip.width = bar_position - x; - } - else - { - clip.y = y; - clip.height = bar_position - y; - } - - gtk_snapshot_push_clip (snapshot, - &GRAPHENE_RECT_INIT( - clip.x, clip.y, - clip.width, clip.height - )); - - gtk_snapshot_render_layout (snapshot, context, - x_pos, y_pos, - layout); - - gtk_snapshot_pop (snapshot); - } - - if (bar_position + bar_size < start + full_size) - { - if (priv->orientation == GTK_ORIENTATION_HORIZONTAL) - { - clip.x = bar_position + bar_size; - clip.width = x + w - (bar_position + bar_size); - } - else - { - clip.y = bar_position + bar_size; - clip.height = y + h - (bar_position + bar_size); - } - - gtk_snapshot_push_clip (snapshot, - &GRAPHENE_RECT_INIT( - clip.x, clip.y, - clip.width, clip.height - )); - - gtk_snapshot_render_layout (snapshot, context, - x_pos, y_pos, - layout); - - gtk_snapshot_pop (snapshot); - } - - gtk_style_context_restore (context); - g_object_unref (layout); - } -} - -static void -sysprof_cell_renderer_progress_class_init (SysprofCellRendererProgressClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (klass); - - object_class->finalize = sysprof_cell_renderer_progress_finalize; - object_class->get_property = sysprof_cell_renderer_progress_get_property; - object_class->set_property = sysprof_cell_renderer_progress_set_property; - - cell_class->get_preferred_width = sysprof_cell_renderer_progress_get_preferred_width; - cell_class->get_preferred_height = sysprof_cell_renderer_progress_get_preferred_height; - cell_class->snapshot = sysprof_cell_renderer_progress_snapshot; - - /** - * SysprofCellRendererProgress:value: - * - * The "value" property determines the percentage to which the - * progress bar will be "filled in". - **/ - g_object_class_install_property (object_class, - PROP_VALUE, - g_param_spec_int ("value", - "Value", - "Value of the progress bar", - 0, 100, 0, - G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); - - /** - * SysprofCellRendererProgress:text: - * - * The "text" property determines the label which will be drawn - * over the progress bar. Setting this property to %NULL causes the default - * label to be displayed. Setting this property to an empty string causes - * no label to be displayed. - **/ - g_object_class_install_property (object_class, - PROP_TEXT, - g_param_spec_string ("text", - "Text", - "Text on the progress bar", - NULL, - G_PARAM_READWRITE)); - - /** - * SysprofCellRendererProgress:pulse: - * - * Setting this to a non-negative value causes the cell renderer to - * enter "activity mode", where a block bounces back and forth to - * indicate that some progress is made, without specifying exactly how - * much. - * - * Each increment of the property causes the block to move by a little - * bit. - * - * To indicate that the activity has not started yet, set the property - * to zero. To indicate completion, set the property to %G_MAXINT. - */ - g_object_class_install_property (object_class, - PROP_PULSE, - g_param_spec_int ("pulse", - "Pulse", - "Set this to positive values to indicate that some progress is made, but you don’t know how much.", - -1, G_MAXINT, -1, - G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); - - /** - * SysprofCellRendererProgress:text-xalign: - * - * The "text-xalign" property controls the horizontal alignment of the - * text in the progress bar. Valid values range from 0 (left) to 1 - * (right). Reserved for RTL layouts. - */ - g_object_class_install_property (object_class, - PROP_TEXT_XALIGN, - g_param_spec_float ("text-xalign", - "Text x alignment", - "The horizontal text alignment, from 0 (left) to 1 (right). Reversed for RTL layouts.", - 0.0, 1.0, 0.5, - G_PARAM_READWRITE)); - - /** - * SysprofCellRendererProgress:text-yalign: - * - * The "text-yalign" property controls the vertical alignment of the - * text in the progress bar. Valid values range from 0 (top) to 1 - * (bottom). - */ - g_object_class_install_property (object_class, - PROP_TEXT_YALIGN, - g_param_spec_float ("text-yalign", - "Text y alignment", - "The vertical text alignment, from 0 (top) to 1 (bottom).", - 0.0, 1.0, 0.5, - G_PARAM_READWRITE)); - - g_object_class_override_property (object_class, - PROP_ORIENTATION, - "orientation"); - - g_object_class_install_property (object_class, - PROP_INVERTED, - g_param_spec_boolean ("inverted", - "Inverted", - "Invert the direction in which the progress bar grows", - FALSE, - G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); -} - -static void -sysprof_cell_renderer_progress_init (SysprofCellRendererProgress *cellprogress) -{ - SysprofCellRendererProgressPrivate *priv = sysprof_cell_renderer_progress_get_instance_private (cellprogress); - - priv->value = 0; - priv->text = NULL; - priv->label = NULL; - priv->min_w = -1; - priv->min_h = -1; - priv->pulse = -1; - priv->offset = 0; - - priv->text_xalign = 0.5; - priv->text_yalign = 0.5; - - priv->orientation = GTK_ORIENTATION_HORIZONTAL, - priv->inverted = FALSE; -} - -/** - * sysprof_cell_renderer_progress_new: - * - * Creates a new `SysprofCellRendererProgress`. - * - * Returns: the new cell renderer - **/ -GtkCellRenderer* -sysprof_cell_renderer_progress_new (void) -{ - return g_object_new (SYSPROF_TYPE_CELL_RENDERER_PROGRESS, NULL); -} diff --git a/src/libsysprof-ui/sysprof-cell-renderer-progress.h b/src/libsysprof-ui/sysprof-cell-renderer-progress.h deleted file mode 100644 index 7a006072..00000000 --- a/src/libsysprof-ui/sysprof-cell-renderer-progress.h +++ /dev/null @@ -1,53 +0,0 @@ -/* gtkcellrendererprogress.h - * Copyright (C) 2002 Naba Kumar - * modified by Jörgen Scheibengruber - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library. If not, see . - */ - -/* - * Modified by the GTK+ Team and others 1997-2004. See the AUTHORS - * file for a list of people on the GTK+ Team. See the ChangeLog - * files for a list of changes. These files are distributed with - * GTK+ at ftp://ftp.gtk.org/pub/gtk/. - */ - -#pragma once - -#include - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_CELL_RENDERER_PROGRESS (sysprof_cell_renderer_progress_get_type ()) -#define SYSPROF_CELL_RENDERER_PROGRESS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SYSPROF_TYPE_CELL_RENDERER_PROGRESS, SysprofCellRendererProgress)) -#define SYSPROF_IS_CELL_RENDERER_PROGRESS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SYSPROF_TYPE_CELL_RENDERER_PROGRESS)) - -typedef struct _SysprofCellRendererProgress SysprofCellRendererProgress; -typedef struct _SysprofCellRendererProgressClass SysprofCellRendererProgressClass; -typedef struct _SysprofCellRendererProgressPrivate SysprofCellRendererProgressPrivate; - -struct _SysprofCellRendererProgress -{ - GtkCellRenderer parent_instance; -}; - -struct _SysprofCellRendererProgressClass -{ - GtkCellRendererClass parent_class; -}; - -GType sysprof_cell_renderer_progress_get_type (void) G_GNUC_CONST; -GtkCellRenderer *sysprof_cell_renderer_progress_new (void); - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-check.c b/src/libsysprof-ui/sysprof-check.c deleted file mode 100644 index d7025790..00000000 --- a/src/libsysprof-ui/sysprof-check.c +++ /dev/null @@ -1,106 +0,0 @@ -/* sysprof-check.c - * - * Copyright 2019 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 - */ - -#define G_LOG_DOMAIN "sysprof-check" - -#include "config.h" - -#include "sysprof-check.h" - -static void -sysprof_check_supported_ping_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - GDBusConnection *bus = (GDBusConnection *)object; - g_autoptr(GVariant) reply = NULL; - g_autoptr(GError) error = NULL; - g_autoptr(GTask) task = user_data; - - g_assert (G_IS_DBUS_CONNECTION (bus)); - g_assert (G_IS_ASYNC_RESULT (result)); - g_assert (G_IS_TASK (task)); - - if (!(reply = g_dbus_connection_call_finish (bus, result, &error))) - g_task_return_error (task, g_steal_pointer (&error)); - else - g_task_return_boolean (task, TRUE); -} - -static void -sysprof_check_supported_bus_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - g_autoptr(GDBusConnection) bus = NULL; - g_autoptr(GError) error = NULL; - g_autoptr(GTask) task = user_data; - - g_assert (G_IS_ASYNC_RESULT (result)); - g_assert (G_IS_TASK (task)); - - if (!(bus = g_bus_get_finish (result, &error))) - g_task_return_error (task, g_steal_pointer (&error)); - else - g_dbus_connection_call (bus, - "org.gnome.Sysprof3", - "/org/gnome/Sysprof3", - "org.freedesktop.DBus.Peer", - "Ping", - g_variant_new ("()"), - NULL, - G_DBUS_CALL_FLAGS_NONE, - -1, - g_task_get_cancellable (task), - sysprof_check_supported_ping_cb, - g_object_ref (task)); -} - -void -sysprof_check_supported_async (GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - g_autoptr(GTask) task = NULL; - - g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); - - task = g_task_new (NULL, cancellable, callback, user_data); - g_task_set_source_tag (task, sysprof_check_supported_async); - - /* Get access to the System D-Bus and check to see if we can ping the - * service that is found at org.gnome.Sysprof3. - */ - - g_bus_get (G_BUS_TYPE_SYSTEM, - cancellable, - sysprof_check_supported_bus_cb, - g_steal_pointer (&task)); - -} - -gboolean -sysprof_check_supported_finish (GAsyncResult *result, - GError **error) -{ - g_return_val_if_fail (G_IS_TASK (result), FALSE); - - return g_task_propagate_boolean (G_TASK (result), error); -} diff --git a/src/libsysprof-ui/sysprof-check.h b/src/libsysprof-ui/sysprof-check.h deleted file mode 100644 index bb8f4e7c..00000000 --- a/src/libsysprof-ui/sysprof-check.h +++ /dev/null @@ -1,37 +0,0 @@ -/* sysprof-check.h - * - * Copyright 2019 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 - -SYSPROF_AVAILABLE_IN_ALL -void sysprof_check_supported_async (GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -SYSPROF_AVAILABLE_IN_ALL -gboolean sysprof_check_supported_finish (GAsyncResult *result, - GError **error); - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-color-cycle.c b/src/libsysprof-ui/sysprof-color-cycle.c deleted file mode 100644 index 582878e9..00000000 --- a/src/libsysprof-ui/sysprof-color-cycle.c +++ /dev/null @@ -1,157 +0,0 @@ -/* sysprof-color-cycle.c - * - * Copyright 2016-2019 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 - */ - -#define G_LOG_DOMAIN "sysprof-color-cycle" - -#include "config.h" - -#include "sysprof-color-cycle.h" - -G_DEFINE_BOXED_TYPE (SysprofColorCycle, sysprof_color_cycle, sysprof_color_cycle_ref, sysprof_color_cycle_unref) - -static const gchar *default_colors[] = { - - "#1a5fb4", /* Blue 5 */ - "#26a269", /* Green 5 */ - "#e5a50a", /* Yellow 5 */ - "#c64600", /* Orange 5 */ - "#a51d2d", /* Red 5 */ - "#613583", /* Purple 5 */ - "#63452c", /* Brown 5 */ - - "#1c71d8", /* Blue 4 */ - "#2ec27e", /* Green 4 */ - "#f5c211", /* Yellow 4 */ - "#e66100", /* Orange 4 */ - "#c01c28", /* Red 4 */ - "#813d9c", /* Purple 4 */ - "#865e3c", /* Brown 4 */ - - "#3584e4", /* Blue 3 */ - "#33d17a", /* Green 3 */ - "#f6d32d", /* Yellow 3 */ - "#ff7800", /* Orange 3 */ - "#e01b24", /* Red 3 */ - "#9141ac", /* Purple 3 */ - "#986a44", /* Brown 3 */ - - "#62a0ea", /* Blue 2 */ - "#57e389", /* Green 2 */ - "#f8e45c", /* Yellow 2 */ - "#ffa348", /* Orange 2 */ - "#ed333b", /* Red 2 */ - "#c061cb", /* Purple 2 */ - "#b5835a", /* Brown 2 */ - - "#99c1f1", /* Blue 1 */ - "#8ff0a4", /* Green 1 */ - "#f9f06b", /* Yellow 1 */ - "#ffbe6f", /* Orange 1 */ - "#f66151", /* Red 1 */ - "#dc8add", /* Purple 1 */ - "#cdab8f", /* Brown 1 */ - - NULL -}; - -struct _SysprofColorCycle -{ - volatile gint ref_count; - GdkRGBA *colors; - gsize n_colors; - guint position; -}; - -static void -sysprof_color_cycle_destroy (SysprofColorCycle *self) -{ - g_free (self->colors); - g_slice_free (SysprofColorCycle, self); -} - -SysprofColorCycle * -sysprof_color_cycle_new (void) -{ - SysprofColorCycle *self; - - self = g_slice_new0 (SysprofColorCycle); - self->ref_count = 1; - self->n_colors = g_strv_length ((gchar **)default_colors); - self->colors = g_new0 (GdkRGBA, self->n_colors); - - for (guint i = 0; default_colors[i]; i++) - { - if G_UNLIKELY (!gdk_rgba_parse (&self->colors[i], default_colors[i])) - g_warning ("Failed to parse color %s into an RGBA", default_colors[i]); - } - - return self; -} - -SysprofColorCycle * -sysprof_color_cycle_ref (SysprofColorCycle *self) -{ - g_return_val_if_fail (self != NULL, NULL); - g_return_val_if_fail (self->ref_count > 0, NULL); - g_atomic_int_inc (&self->ref_count); - return self; -} - -void -sysprof_color_cycle_unref (SysprofColorCycle *self) -{ - g_return_if_fail (self != NULL); - g_return_if_fail (self->ref_count > 0); - if (g_atomic_int_dec_and_test (&self->ref_count)) - sysprof_color_cycle_destroy (self); -} - -void -sysprof_color_cycle_next (SysprofColorCycle *self, - GdkRGBA *rgba) -{ - g_return_if_fail (self != NULL); - g_return_if_fail (self->position < self->n_colors); - - *rgba = self->colors[self->position]; - - /* - * TODO: Adjust color HSV/etc - * - * We could simply adjust the brightness/etc after we dispatch - * a color so that we get darker as we go. - */ - - self->position = (self->position + 1) % self->n_colors; -} - -void -sysprof_color_cycle_reset (SysprofColorCycle *self) -{ - g_return_if_fail (self != NULL); - - for (guint i = 0; default_colors[i]; i++) - { - if G_UNLIKELY (!gdk_rgba_parse (&self->colors[i], default_colors[i])) - g_warning ("Failed to parse color %s into an RGBA", default_colors[i]); - } - - self->position = 0; -} diff --git a/src/libsysprof-ui/sysprof-color-cycle.h b/src/libsysprof-ui/sysprof-color-cycle.h deleted file mode 100644 index 650acb78..00000000 --- a/src/libsysprof-ui/sysprof-color-cycle.h +++ /dev/null @@ -1,41 +0,0 @@ -/* sysprof-color-cycle.h - * - * Copyright 2016-2019 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_COLOR_CYCLE (sysprof_color_cycle_get_type()) - -typedef struct _SysprofColorCycle SysprofColorCycle; - -GType sysprof_color_cycle_get_type (void); -SysprofColorCycle *sysprof_color_cycle_ref (SysprofColorCycle *self); -void sysprof_color_cycle_unref (SysprofColorCycle *self); -SysprofColorCycle *sysprof_color_cycle_new (void); -void sysprof_color_cycle_reset (SysprofColorCycle *self); -void sysprof_color_cycle_next (SysprofColorCycle *self, - GdkRGBA *rgba); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofColorCycle, sysprof_color_cycle_unref) - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-counters-aid.c b/src/libsysprof-ui/sysprof-counters-aid.c deleted file mode 100644 index 42bd04f0..00000000 --- a/src/libsysprof-ui/sysprof-counters-aid.c +++ /dev/null @@ -1,284 +0,0 @@ -/* sysprof-counters-aid.c - * - * Copyright 2019 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 - */ - -#define G_LOG_DOMAIN "sysprof-counters-aid" - -#include "config.h" - -#include - -#include "sysprof-color-cycle.h" -#include "sysprof-counters-aid.h" -#include "sysprof-line-visualizer.h" -#include "sysprof-marks-page.h" -#include "sysprof-time-visualizer.h" - -struct _SysprofCountersAid -{ - SysprofAid parent_instance; -}; - -typedef struct -{ - SysprofCaptureCursor *cursor; - SysprofDisplay *display; -} Present; - -G_DEFINE_TYPE (SysprofCountersAid, sysprof_counters_aid, SYSPROF_TYPE_AID) - -static void -present_free (gpointer data) -{ - Present *p = data; - - g_clear_pointer (&p->cursor, sysprof_capture_cursor_unref); - g_clear_object (&p->display); - g_slice_free (Present, p); -} - -static void -on_group_activated_cb (SysprofVisualizerGroup *group, - SysprofPage *page) -{ - SysprofDisplay *display; - - g_assert (SYSPROF_IS_VISUALIZER_GROUP (group)); - g_assert (SYSPROF_IS_PAGE (page)); - - display = SYSPROF_DISPLAY (gtk_widget_get_ancestor (GTK_WIDGET (page), SYSPROF_TYPE_DISPLAY)); - sysprof_display_set_visible_page (display, page); -} - -/** - * sysprof_counters_aid_new: - * - * Create a new #SysprofCountersAid. - * - * Returns: (transfer full): a newly created #SysprofCountersAid - * - * Since: 3.34 - */ -SysprofAid * -sysprof_counters_aid_new (void) -{ - return g_object_new (SYSPROF_TYPE_COUNTERS_AID, NULL); -} - -static void -sysprof_counters_aid_prepare (SysprofAid *self, - SysprofProfiler *profiler) -{ -} - -static gchar * -build_title (const SysprofCaptureCounter *ctr) -{ - GString *str; - - str = g_string_new (NULL); - - if (ctr->category[0] != 0) - { - if (str->len) - g_string_append_c (str, ' '); - g_string_append (str, ctr->category); - } - - if (ctr->name[0] != 0) - { - if (str->len) - g_string_append (str, " — "); - g_string_append (str, ctr->name); - } - - if (ctr->description[0] != 0) - { - if (str->len) - g_string_append_printf (str, " (%s)", ctr->description); - else - g_string_append (str, ctr->description); - } - - if (str->len == 0) - /* this is untranslated on purpose */ - g_string_append_printf (str, "Counter %d", ctr->id); - - return g_string_free (str, FALSE); -} - -static bool -collect_counters (const SysprofCaptureFrame *frame, - gpointer user_data) -{ - SysprofCaptureCounterDefine *def = (SysprofCaptureCounterDefine *)frame; - GArray *counters = user_data; - - g_assert (frame != NULL); - g_assert (frame->type == SYSPROF_CAPTURE_FRAME_CTRDEF); - g_assert (counters != NULL); - - if (def->n_counters > 0) - g_array_append_vals (counters, def->counters, def->n_counters); - - return TRUE; -} - -static void -sysprof_counters_aid_present_worker (GTask *task, - gpointer source_object, - gpointer task_data, - GCancellable *cancellable) -{ - Present *present = task_data; - g_autoptr(GArray) counters = NULL; - - g_assert (G_IS_TASK (task)); - g_assert (SYSPROF_IS_COUNTERS_AID (source_object)); - g_assert (present != NULL); - g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); - - counters = g_array_new (FALSE, FALSE, sizeof (SysprofCaptureCounter)); - sysprof_capture_cursor_foreach (present->cursor, collect_counters, counters); - g_task_return_pointer (task, - g_steal_pointer (&counters), - (GDestroyNotify) g_array_unref); -} - -static void -sysprof_counters_aid_present_async (SysprofAid *aid, - SysprofCaptureReader *reader, - SysprofDisplay *display, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - static const SysprofCaptureFrameType types[] = { SYSPROF_CAPTURE_FRAME_CTRDEF }; - g_autoptr(SysprofCaptureCondition) condition = NULL; - g_autoptr(SysprofCaptureCursor) cursor = NULL; - g_autoptr(GTask) task = NULL; - Present present; - - g_assert (SYSPROF_IS_COUNTERS_AID (aid)); - g_assert (reader != NULL); - g_assert (SYSPROF_IS_DISPLAY (display)); - g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); - - condition = sysprof_capture_condition_new_where_type_in (1, types); - cursor = sysprof_capture_cursor_new (reader); - sysprof_capture_cursor_add_condition (cursor, g_steal_pointer (&condition)); - - present.cursor = g_steal_pointer (&cursor); - present.display = g_object_ref (display); - - task = g_task_new (aid, cancellable, callback, user_data); - g_task_set_source_tag (task, sysprof_counters_aid_present_async); - g_task_set_task_data (task, - g_slice_dup (Present, &present), - present_free); - g_task_run_in_thread (task, sysprof_counters_aid_present_worker); -} - -static gboolean -sysprof_counters_aid_present_finish (SysprofAid *aid, - GAsyncResult *result, - GError **error) -{ - g_autoptr(GArray) counters = NULL; - Present *present; - - g_assert (SYSPROF_IS_AID (aid)); - g_assert (G_IS_TASK (result)); - - present = g_task_get_task_data (G_TASK (result)); - - if ((counters = g_task_propagate_pointer (G_TASK (result), error)) && counters->len > 0) - { - g_autoptr(SysprofColorCycle) cycle = sysprof_color_cycle_new (); - SysprofVisualizerGroup *group; - SysprofVisualizer *combined; - GtkWidget *page; - - group = g_object_new (SYSPROF_TYPE_VISUALIZER_GROUP, - "can-focus", TRUE, - "has-page", TRUE, - "title", _("Counters"), - "visible", TRUE, - NULL); - - combined = g_object_new (SYSPROF_TYPE_TIME_VISUALIZER, - "title", _("Counters"), - "height-request", 35, - "visible", TRUE, - NULL); - sysprof_visualizer_group_insert (group, combined, -1, TRUE); - - for (guint i = 0; i < counters->len; i++) - { - const SysprofCaptureCounter *ctr = &g_array_index (counters, SysprofCaptureCounter, i); - g_autofree gchar *title = build_title (ctr); - GtkWidget *row; - GdkRGBA rgba; - - row = g_object_new (SYSPROF_TYPE_LINE_VISUALIZER, - "title", title, - "height-request", 35, - "visible", FALSE, - NULL); - sysprof_color_cycle_next (cycle, &rgba); - sysprof_line_visualizer_add_counter (SYSPROF_LINE_VISUALIZER (row), ctr->id, &rgba); - rgba.alpha = .5; - sysprof_line_visualizer_set_fill (SYSPROF_LINE_VISUALIZER (row), ctr->id, &rgba); - sysprof_time_visualizer_add_counter (SYSPROF_TIME_VISUALIZER (combined), ctr->id, &rgba); - sysprof_visualizer_group_insert (group, SYSPROF_VISUALIZER (row), -1, TRUE); - } - - sysprof_display_add_group (present->display, group); - - page = sysprof_marks_page_new (sysprof_display_get_zoom_manager (present->display), - SYSPROF_MARKS_MODEL_COUNTERS); - gtk_widget_show (page); - - g_signal_connect_object (group, - "group-activated", - G_CALLBACK (on_group_activated_cb), - page, - 0); - sysprof_display_add_page (present->display, SYSPROF_PAGE (page)); - } - - return counters != NULL; -} - -static void -sysprof_counters_aid_class_init (SysprofCountersAidClass *klass) -{ - SysprofAidClass *aid_class = SYSPROF_AID_CLASS (klass); - - aid_class->prepare = sysprof_counters_aid_prepare; - aid_class->present_async = sysprof_counters_aid_present_async; - aid_class->present_finish = sysprof_counters_aid_present_finish; -} - -static void -sysprof_counters_aid_init (SysprofCountersAid *self) -{ - sysprof_aid_set_display_name (SYSPROF_AID (self), _("Counters")); - sysprof_aid_set_icon_name (SYSPROF_AID (self), "org.gnome.Sysprof-symbolic"); -} diff --git a/src/libsysprof-ui/sysprof-counters-aid.h b/src/libsysprof-ui/sysprof-counters-aid.h deleted file mode 100644 index 5541b60f..00000000 --- a/src/libsysprof-ui/sysprof-counters-aid.h +++ /dev/null @@ -1,33 +0,0 @@ -/* sysprof-counters-aid.h - * - * Copyright 2019 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-aid.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_COUNTERS_AID (sysprof_counters_aid_get_type()) - -G_DECLARE_FINAL_TYPE (SysprofCountersAid, sysprof_counters_aid, SYSPROF, COUNTERS_AID, SysprofAid) - -SysprofAid *sysprof_counters_aid_new (void); - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-cpu-aid.c b/src/libsysprof-ui/sysprof-cpu-aid.c deleted file mode 100644 index febdf876..00000000 --- a/src/libsysprof-ui/sysprof-cpu-aid.c +++ /dev/null @@ -1,357 +0,0 @@ -/* sysprof-cpu-aid.c - * - * Copyright 2019 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 - */ - -#define G_LOG_DOMAIN "sysprof-cpu-aid" - -#include "config.h" - -#include - -#include "sysprof-color-cycle.h" -#include "sysprof-cpu-aid.h" -#include "sysprof-line-visualizer.h" -#include "sysprof-procs-visualizer.h" - -struct _SysprofCpuAid -{ - SysprofAid parent_instance; -}; - -typedef struct -{ - SysprofCaptureCursor *cursor; - SysprofDisplay *display; - GArray *counters; - guint has_processes : 1; -} Present; - -G_DEFINE_TYPE (SysprofCpuAid, sysprof_cpu_aid, SYSPROF_TYPE_AID) - -static void -present_free (gpointer data) -{ - Present *p = data; - - g_clear_pointer (&p->cursor, sysprof_capture_cursor_unref); - g_clear_pointer (&p->counters, g_array_unref); - g_clear_object (&p->display); - g_slice_free (Present, p); -} - -/** - * sysprof_cpu_aid_new: - * - * Create a new #SysprofCpuAid. - * - * Returns: (transfer full): a newly created #SysprofCpuAid - * - * Since: 3.34 - */ -SysprofAid * -sysprof_cpu_aid_new (void) -{ - return g_object_new (SYSPROF_TYPE_CPU_AID, NULL); -} - -static void -sysprof_cpu_aid_prepare (SysprofAid *self, - SysprofProfiler *profiler) -{ -#ifdef __linux__ - g_autoptr(SysprofSource) source = NULL; - - g_assert (SYSPROF_IS_CPU_AID (self)); - g_assert (SYSPROF_IS_PROFILER (profiler)); - - source = sysprof_hostinfo_source_new (); - sysprof_profiler_add_source (profiler, source); -#endif -} - -static bool -collect_info (const SysprofCaptureFrame *frame, - gpointer user_data) -{ - SysprofCaptureCounterDefine *def = (SysprofCaptureCounterDefine *)frame; - Present *p = user_data; - - g_assert (frame != NULL); - g_assert (p != NULL); - g_assert (p->counters != NULL); - - if (frame->type == SYSPROF_CAPTURE_FRAME_CTRDEF) - { - for (guint i = 0; i < def->n_counters; i++) - { - const SysprofCaptureCounter *counter = &def->counters[i]; - - if (g_strcmp0 (counter->category, "CPU Percent") == 0 || - g_strcmp0 (counter->category, "CPU Frequency") == 0) - g_array_append_vals (p->counters, counter, 1); - } - } - else if (!p->has_processes && - (frame->type == SYSPROF_CAPTURE_FRAME_PROCESS || - frame->type == SYSPROF_CAPTURE_FRAME_EXIT)) - { - p->has_processes = TRUE; - } - - return TRUE; -} - -static void -sysprof_cpu_aid_present_worker (GTask *task, - gpointer source_object, - gpointer task_data, - GCancellable *cancellable) -{ - Present *present = task_data; - - g_assert (G_IS_TASK (task)); - g_assert (SYSPROF_IS_CPU_AID (source_object)); - g_assert (present != NULL); - g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); - - sysprof_capture_cursor_foreach (present->cursor, collect_info, present); - g_task_return_pointer (task, - g_steal_pointer (&present->counters), - (GDestroyNotify) g_array_unref); -} - -static void -sysprof_cpu_aid_present_async (SysprofAid *aid, - SysprofCaptureReader *reader, - SysprofDisplay *display, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - static const SysprofCaptureFrameType types[] = { - SYSPROF_CAPTURE_FRAME_CTRDEF, - SYSPROF_CAPTURE_FRAME_PROCESS, - SYSPROF_CAPTURE_FRAME_EXIT, - }; - g_autoptr(SysprofCaptureCondition) condition = NULL; - g_autoptr(SysprofCaptureCursor) cursor = NULL; - g_autoptr(GTask) task = NULL; - Present present; - - g_assert (SYSPROF_IS_CPU_AID (aid)); - g_assert (reader != NULL); - g_assert (SYSPROF_IS_DISPLAY (display)); - g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); - - condition = sysprof_capture_condition_new_where_type_in (G_N_ELEMENTS (types), types); - cursor = sysprof_capture_cursor_new (reader); - sysprof_capture_cursor_add_condition (cursor, g_steal_pointer (&condition)); - - present.cursor = g_steal_pointer (&cursor); - present.display = g_object_ref (display); - present.counters = g_array_new (FALSE, FALSE, sizeof (SysprofCaptureCounter)); - present.has_processes = FALSE; - - task = g_task_new (aid, cancellable, callback, user_data); - g_task_set_source_tag (task, sysprof_cpu_aid_present_async); - g_task_set_task_data (task, - g_slice_dup (Present, &present), - present_free); - g_task_run_in_thread (task, sysprof_cpu_aid_present_worker); -} - -static gboolean -sysprof_cpu_aid_present_finish (SysprofAid *aid, - GAsyncResult *result, - GError **error) -{ - g_autoptr(GArray) counters = NULL; - Present *present; - - g_assert (SYSPROF_IS_AID (aid)); - g_assert (G_IS_TASK (result)); - - present = g_task_get_task_data (G_TASK (result)); - - if ((counters = g_task_propagate_pointer (G_TASK (result), error))) - { - g_autoptr(SysprofColorCycle) cycle = sysprof_color_cycle_new (); - g_autoptr(SysprofColorCycle) freq_cycle = sysprof_color_cycle_new (); - SysprofVisualizerGroup *usage; - SysprofVisualizerGroup *freq; - SysprofVisualizer *freq_row = NULL; - SysprofVisualizer *over_row = NULL; - gboolean found_combined = FALSE; - gboolean has_usage = FALSE; - gboolean has_freq = FALSE; - - usage = g_object_new (SYSPROF_TYPE_VISUALIZER_GROUP, - "can-focus", TRUE, - "priority", -1000, - "title", _("CPU Usage"), - "visible", TRUE, - NULL); - - freq = g_object_new (SYSPROF_TYPE_VISUALIZER_GROUP, - "can-focus", TRUE, - "priority", -999, - "title", _("CPU Frequency"), - "visible", TRUE, - NULL); - freq_row = g_object_new (SYSPROF_TYPE_LINE_VISUALIZER, - "title", _("CPU Frequency (All)"), - "height-request", 35, - "visible", TRUE, - "y-lower", 0.0, - "y-upper", 100.0, - NULL); - sysprof_visualizer_group_insert (freq, freq_row, -1, FALSE); - - over_row = g_object_new (SYSPROF_TYPE_LINE_VISUALIZER, - "title", _("CPU Usage (All)"), - "height-request", 35, - "visible", TRUE, - "y-lower", 0.0, - "y-upper", 100.0, - NULL); - - for (guint i = 0; i < counters->len; i++) - { - const SysprofCaptureCounter *ctr = &g_array_index (counters, SysprofCaptureCounter, i); - - if (g_strcmp0 (ctr->category, "CPU Percent") == 0) - { - if (strstr (ctr->name, "Combined") != NULL) - { - GtkWidget *row; - GdkRGBA rgba; - - found_combined = TRUE; - - gdk_rgba_parse (&rgba, "#1a5fb4"); - row = g_object_new (SYSPROF_TYPE_LINE_VISUALIZER, - /* Translators: CPU is the processor. */ - "title", _("CPU Usage (All)"), - "height-request", 35, - "visible", TRUE, - "y-lower", 0.0, - "y-upper", 100.0, - NULL); - sysprof_line_visualizer_add_counter (SYSPROF_LINE_VISUALIZER (row), ctr->id, &rgba); - rgba.alpha = 0.5; - sysprof_line_visualizer_set_fill (SYSPROF_LINE_VISUALIZER (row), ctr->id, &rgba); - sysprof_visualizer_group_insert (usage, SYSPROF_VISUALIZER (row), 0, FALSE); - has_usage = TRUE; - } - else if (g_str_has_prefix (ctr->name, "Total CPU ")) - { - GtkWidget *row; - GdkRGBA rgba; - - sysprof_color_cycle_next (cycle, &rgba); - row = g_object_new (SYSPROF_TYPE_LINE_VISUALIZER, - "title", ctr->name, - "height-request", 35, - "visible", FALSE, - "y-lower", 0.0, - "y-upper", 100.0, - NULL); - sysprof_line_visualizer_add_counter (SYSPROF_LINE_VISUALIZER (row), ctr->id, &rgba); - sysprof_line_visualizer_add_counter (SYSPROF_LINE_VISUALIZER (over_row), ctr->id, &rgba); - rgba.alpha = 0.5; - sysprof_line_visualizer_set_fill (SYSPROF_LINE_VISUALIZER (row), ctr->id, &rgba); - sysprof_visualizer_group_insert (usage, SYSPROF_VISUALIZER (row), -1, TRUE); - has_usage = TRUE; - } - } - else if (g_strcmp0 (ctr->category, "CPU Frequency") == 0) - { - if (g_str_has_prefix (ctr->name, "CPU ")) - { - g_autofree gchar *title = g_strdup_printf ("%s Frequency", ctr->name); - GtkWidget *row; - GdkRGBA rgba; - - sysprof_color_cycle_next (freq_cycle, &rgba); - sysprof_line_visualizer_add_counter (SYSPROF_LINE_VISUALIZER (freq_row), ctr->id, &rgba); - sysprof_line_visualizer_set_dash (SYSPROF_LINE_VISUALIZER (freq_row), ctr->id, TRUE); - - row = g_object_new (SYSPROF_TYPE_LINE_VISUALIZER, - "title", title, - "height-request", 35, - "visible", FALSE, - "y-lower", 0.0, - "y-upper", 100.0, - NULL); - sysprof_line_visualizer_add_counter (SYSPROF_LINE_VISUALIZER (row), ctr->id, &rgba); - sysprof_line_visualizer_set_dash (SYSPROF_LINE_VISUALIZER (row), ctr->id, TRUE); - sysprof_visualizer_group_insert (freq, SYSPROF_VISUALIZER (row), -1, TRUE); - - has_freq = TRUE; - } - } - } - - if (present->has_processes) - { - GtkWidget *row; - - row = g_object_new (SYSPROF_TYPE_PROCS_VISUALIZER, - "title", _("Processes"), - "height-request", 35, - "visible", FALSE, - NULL); - sysprof_visualizer_group_insert (usage, SYSPROF_VISUALIZER (row), -1, TRUE); - } - - if (has_usage && !found_combined) - sysprof_visualizer_group_insert (usage, over_row, 0, FALSE); - else - g_object_unref (g_object_ref_sink (over_row)); - - if (has_usage) - sysprof_display_add_group (present->display, usage); - else - g_object_unref (g_object_ref_sink (usage)); - - if (has_freq) - sysprof_display_add_group (present->display, freq); - else - g_object_unref (g_object_ref_sink (freq)); - } - - return counters != NULL; -} - -static void -sysprof_cpu_aid_class_init (SysprofCpuAidClass *klass) -{ - SysprofAidClass *aid_class = SYSPROF_AID_CLASS (klass); - - aid_class->prepare = sysprof_cpu_aid_prepare; - aid_class->present_async = sysprof_cpu_aid_present_async; - aid_class->present_finish = sysprof_cpu_aid_present_finish; -} - -static void -sysprof_cpu_aid_init (SysprofCpuAid *self) -{ - sysprof_aid_set_display_name (SYSPROF_AID (self), _("CPU Usage")); - sysprof_aid_set_icon_name (SYSPROF_AID (self), "org.gnome.Sysprof-symbolic"); -} diff --git a/src/libsysprof-ui/sysprof-cpu-aid.h b/src/libsysprof-ui/sysprof-cpu-aid.h deleted file mode 100644 index 4769885e..00000000 --- a/src/libsysprof-ui/sysprof-cpu-aid.h +++ /dev/null @@ -1,33 +0,0 @@ -/* sysprof-cpu-aid.h - * - * Copyright 2019 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-aid.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_CPU_AID (sysprof_cpu_aid_get_type()) - -G_DECLARE_FINAL_TYPE (SysprofCpuAid, sysprof_cpu_aid, SYSPROF, CPU_AID, SysprofAid) - -SysprofAid *sysprof_cpu_aid_new (void); - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-depth-visualizer.c b/src/libsysprof-ui/sysprof-depth-visualizer.c deleted file mode 100644 index c98e46d8..00000000 --- a/src/libsysprof-ui/sysprof-depth-visualizer.c +++ /dev/null @@ -1,453 +0,0 @@ -/* sysprof-depth-visualizer.c - * - * Copyright 2019 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 - */ - -#define G_LOG_DOMAIN "sysprof-depth-visualizer" - -#include "config.h" - -#include - -#include "pointcache.h" -#include "sysprof-depth-visualizer.h" - -struct _SysprofDepthVisualizer -{ - SysprofVisualizer parent_instance; - SysprofCaptureReader *reader; - PointCache *points; - guint reload_source; - guint mode; - int last_width; - int last_height; - guint reloading : 1; - guint needs_reload : 1; -}; - -typedef struct -{ - SysprofCaptureReader *reader; - PointCache *pc; - gint64 begin_time; - gint64 end_time; - gint64 duration; - guint max_n_addrs; - guint mode; -} State; - -static void sysprof_depth_visualizer_reload (SysprofDepthVisualizer *self); - -G_DEFINE_TYPE (SysprofDepthVisualizer, sysprof_depth_visualizer, SYSPROF_TYPE_VISUALIZER) - -static void -state_free (State *st) -{ - g_clear_pointer (&st->reader, sysprof_capture_reader_unref); - g_clear_pointer (&st->pc, point_cache_unref); - g_slice_free (State, st); -} - -static bool -discover_max_n_addr (const SysprofCaptureFrame *frame, - gpointer user_data) -{ - const SysprofCaptureSample *sample = (const SysprofCaptureSample *)frame; - State *st = user_data; - - g_assert (frame != NULL); - g_assert (frame->type == SYSPROF_CAPTURE_FRAME_SAMPLE); - g_assert (st != NULL); - - st->max_n_addrs = MAX (st->max_n_addrs, sample->n_addrs); - - return TRUE; -} - -static bool -build_point_cache_cb (const SysprofCaptureFrame *frame, - gpointer user_data) -{ - const SysprofCaptureSample *sample = (const SysprofCaptureSample *)frame; - State *st = user_data; - gdouble x, y; - gboolean has_kernel = FALSE; - - g_assert (frame != NULL); - g_assert (frame->type == SYSPROF_CAPTURE_FRAME_SAMPLE); - g_assert (st != NULL); - - x = (frame->time - st->begin_time) / (gdouble)st->duration; - y = sample->n_addrs / (gdouble)st->max_n_addrs; - - /* If this contains a context-switch (meaning we're going into the kernel - * to do some work, use a negative value for Y so that we know later on - * that we should draw it with a different color (after removing the negation - * on the value. - * - * We skip past the first index, which is always a context switch as it is - * our perf handler. - */ - for (guint i = 1; i < sample->n_addrs; i++) - { - SysprofAddressContext kind; - - if (sysprof_address_is_context_switch (sample->addrs[i], &kind)) - { - has_kernel = TRUE; - y = -y; - break; - } - } - - if (!has_kernel) - point_cache_add_point_to_set (st->pc, 1, x, y); - else - point_cache_add_point_to_set (st->pc, 2, x, y); - - return TRUE; -} - -static void -sysprof_depth_visualizer_worker (GTask *task, - gpointer source_object, - gpointer task_data, - GCancellable *cancellable) -{ - static const SysprofCaptureFrameType types[] = { SYSPROF_CAPTURE_FRAME_SAMPLE, }; - g_autoptr(SysprofCaptureCursor) cursor = NULL; - SysprofCaptureCondition *condition; - State *st = task_data; - - g_assert (G_IS_TASK (task)); - g_assert (SYSPROF_IS_DEPTH_VISUALIZER (source_object)); - g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); - - if (st->duration != 0) - { - cursor = sysprof_capture_cursor_new (st->reader); - condition = sysprof_capture_condition_new_where_type_in (G_N_ELEMENTS (types), types); - sysprof_capture_cursor_add_condition (cursor, g_steal_pointer (&condition)); - - sysprof_capture_cursor_foreach (cursor, discover_max_n_addr, st); - sysprof_capture_cursor_reset (cursor); - sysprof_capture_cursor_foreach (cursor, build_point_cache_cb, st); - } - - g_task_return_pointer (task, - g_steal_pointer (&st->pc), - (GDestroyNotify) point_cache_unref); -} - -static void -apply_point_cache_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - SysprofDepthVisualizer *self = (SysprofDepthVisualizer *)object; - PointCache *pc; - - g_assert (SYSPROF_IS_DEPTH_VISUALIZER (self)); - g_assert (G_IS_TASK (result)); - - self->reloading = FALSE; - - if ((pc = g_task_propagate_pointer (G_TASK (result), NULL))) - { - g_clear_pointer (&self->points, point_cache_unref); - self->points = g_steal_pointer (&pc); - gtk_widget_queue_draw (GTK_WIDGET (self)); - } - - if (self->needs_reload) - sysprof_depth_visualizer_reload (self); -} - -static void -sysprof_depth_visualizer_reload (SysprofDepthVisualizer *self) -{ - g_autoptr(GTask) task = NULL; - GtkAllocation alloc; - State *st; - - g_assert (SYSPROF_IS_DEPTH_VISUALIZER (self)); - - self->needs_reload = TRUE; - - if (self->reloading) - return; - - self->reloading = TRUE; - self->needs_reload = FALSE; - - gtk_widget_get_allocation (GTK_WIDGET (self), &alloc); - - st = g_slice_new0 (State); - st->reader = sysprof_capture_reader_ref (self->reader); - st->pc = point_cache_new (); - st->max_n_addrs = 0; - st->begin_time = sysprof_capture_reader_get_start_time (self->reader); - st->end_time = sysprof_capture_reader_get_end_time (self->reader); - st->duration = st->end_time - st->begin_time; - st->mode = self->mode; - - point_cache_add_set (st->pc, 1); - point_cache_add_set (st->pc, 2); - - task = g_task_new (self, NULL, apply_point_cache_cb, NULL); - g_task_set_source_tag (task, sysprof_depth_visualizer_reload); - g_task_set_task_data (task, st, (GDestroyNotify) state_free); - g_task_run_in_thread (task, sysprof_depth_visualizer_worker); -} - -static void -sysprof_depth_visualizer_set_reader (SysprofVisualizer *row, - SysprofCaptureReader *reader) -{ - SysprofDepthVisualizer *self = (SysprofDepthVisualizer *)row; - - g_assert (SYSPROF_IS_DEPTH_VISUALIZER (self)); - - if (self->reader != reader) - { - if (self->reader != NULL) - { - sysprof_capture_reader_unref (self->reader); - self->reader = NULL; - } - - if (reader != NULL) - { - self->reader = sysprof_capture_reader_ref (reader); - sysprof_depth_visualizer_reload (self); - } - } -} - -static void -sysprof_depth_visualizer_snapshot (GtkWidget *widget, - GtkSnapshot *snapshot) -{ - SysprofDepthVisualizer *self = (SysprofDepthVisualizer *)widget; - GtkAllocation alloc; - GdkRectangle clip; - const Point *points; - cairo_t *cr; - guint n_points = 0; - GdkRGBA user; - GdkRGBA system; - - g_assert (SYSPROF_IS_DEPTH_VISUALIZER (self)); - g_assert (snapshot != NULL); - - GTK_WIDGET_CLASS (sysprof_depth_visualizer_parent_class)->snapshot (widget, snapshot); - - if (self->points == NULL) - return; - - gdk_rgba_parse (&user, "#1a5fb4"); - gdk_rgba_parse (&system, "#3584e4"); - - gtk_widget_get_allocation (widget, &alloc); - - cr = gtk_snapshot_append_cairo (snapshot, &GRAPHENE_RECT_INIT (0, 0, alloc.width, alloc.height)); - - /* FIXME: we should abstract visualizer drawing into regions so that we - * can still know the region we're drawing. - */ -#if 0 - if (!gdk_cairo_get_clip_rectangle (cr, &clip)) - return; -#else - clip.x = alloc.x = 0; - clip.y = alloc.y = 0; - clip.width = alloc.width; - clip.height = alloc.height; -#endif - - /* Draw user-space stacks */ - if (self->mode != SYSPROF_DEPTH_VISUALIZER_KERNEL_ONLY && - (points = point_cache_get_points (self->points, 1, &n_points))) - { - g_autofree SysprofVisualizerAbsolutePoint *out_points = NULL; - - out_points = g_new (SysprofVisualizerAbsolutePoint, n_points); - sysprof_visualizer_translate_points (SYSPROF_VISUALIZER (widget), - (const SysprofVisualizerRelativePoint *)points, - n_points, out_points, n_points); - - cairo_set_line_width (cr, 1.0); - gdk_cairo_set_source_rgba (cr, &user); - - for (guint i = 0; i < n_points; i++) - { - gdouble x, y; - - x = out_points[i].x; - y = out_points[i].y; - - if (x < clip.x) - continue; - - if (x > clip.x + clip.width) - break; - - for (guint j = i + 1; j < n_points; j++) - { - if (out_points[j].x != x) - break; - - y = MIN (y, out_points[j].y); - } - - x += alloc.x; - - cairo_move_to (cr, (guint)x + .5, alloc.height); - cairo_line_to (cr, (guint)x + .5, y); - } - - cairo_stroke (cr); - } - - /* Draw kernel-space stacks */ - if (self->mode != SYSPROF_DEPTH_VISUALIZER_USER_ONLY && - (points = point_cache_get_points (self->points, 2, &n_points))) - { - g_autofree SysprofVisualizerAbsolutePoint *out_points = NULL; - - out_points = g_new (SysprofVisualizerAbsolutePoint, n_points); - sysprof_visualizer_translate_points (SYSPROF_VISUALIZER (widget), - (const SysprofVisualizerRelativePoint *)points, - n_points, out_points, n_points); - - cairo_set_line_width (cr, 1.0); - gdk_cairo_set_source_rgba (cr, &system); - - for (guint i = 0; i < n_points; i++) - { - gdouble x, y; - - x = out_points[i].x; - y = out_points[i].y; - - if (x < clip.x) - continue; - - if (x > clip.x + clip.width) - break; - - for (guint j = i + 1; j < n_points; j++) - { - if (out_points[j].x != x) - break; - - y = MIN (y, out_points[j].y); - } - - x += alloc.x; - - cairo_move_to (cr, (guint)x + .5, alloc.height); - cairo_line_to (cr, (guint)x + .5, y); - } - - cairo_stroke (cr); - } - - cairo_destroy (cr); -} - -static gboolean -sysprof_depth_visualizer_do_reload (gpointer data) -{ - SysprofDepthVisualizer *self = data; - self->reload_source = 0; - sysprof_depth_visualizer_reload (self); - return G_SOURCE_REMOVE; -} - -static void -sysprof_depth_visualizer_queue_reload (SysprofDepthVisualizer *self) -{ - g_assert (SYSPROF_IS_DEPTH_VISUALIZER (self)); - - g_clear_handle_id (&self->reload_source, g_source_remove); - self->reload_source = g_idle_add (sysprof_depth_visualizer_do_reload, self); -} - -static void -sysprof_depth_visualizer_size_allocate (GtkWidget *widget, - int width, - int height, - int baseline) -{ - SysprofDepthVisualizer *self = (SysprofDepthVisualizer *)widget; - - if (width != self->last_width || height != self->last_height) - { - sysprof_depth_visualizer_queue_reload (SYSPROF_DEPTH_VISUALIZER (widget)); - self->last_width = width; - self->last_height = height; - } -} - -static void -sysprof_depth_visualizer_finalize (GObject *object) -{ - SysprofDepthVisualizer *self = (SysprofDepthVisualizer *)object; - - g_clear_pointer (&self->reader, sysprof_capture_reader_unref); - g_clear_handle_id (&self->reload_source, g_source_remove); - - G_OBJECT_CLASS (sysprof_depth_visualizer_parent_class)->finalize (object); -} - -static void -sysprof_depth_visualizer_class_init (SysprofDepthVisualizerClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); - SysprofVisualizerClass *row_class = SYSPROF_VISUALIZER_CLASS (klass); - - object_class->finalize = sysprof_depth_visualizer_finalize; - - widget_class->snapshot = sysprof_depth_visualizer_snapshot; - widget_class->size_allocate = sysprof_depth_visualizer_size_allocate; - - row_class->set_reader = sysprof_depth_visualizer_set_reader; -} - -static void -sysprof_depth_visualizer_init (SysprofDepthVisualizer *self) -{ -} - -SysprofVisualizer * -sysprof_depth_visualizer_new (SysprofDepthVisualizerMode mode) -{ - SysprofDepthVisualizer *self; - - g_return_val_if_fail (mode == SYSPROF_DEPTH_VISUALIZER_COMBINED || - mode == SYSPROF_DEPTH_VISUALIZER_KERNEL_ONLY || - mode == SYSPROF_DEPTH_VISUALIZER_USER_ONLY, - NULL); - - self = g_object_new (SYSPROF_TYPE_DEPTH_VISUALIZER, NULL); - self->mode = mode; - - return SYSPROF_VISUALIZER (g_steal_pointer (&self)); -} diff --git a/src/libsysprof-ui/sysprof-depth-visualizer.h b/src/libsysprof-ui/sysprof-depth-visualizer.h deleted file mode 100644 index 425c4d42..00000000 --- a/src/libsysprof-ui/sysprof-depth-visualizer.h +++ /dev/null @@ -1,40 +0,0 @@ -/* sysprof-depth-visualizer.h - * - * Copyright 2019 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-visualizer.h" - -G_BEGIN_DECLS - -typedef enum -{ - SYSPROF_DEPTH_VISUALIZER_COMBINED, - SYSPROF_DEPTH_VISUALIZER_KERNEL_ONLY, - SYSPROF_DEPTH_VISUALIZER_USER_ONLY, -} SysprofDepthVisualizerMode; - -#define SYSPROF_TYPE_DEPTH_VISUALIZER (sysprof_depth_visualizer_get_type()) - -G_DECLARE_FINAL_TYPE (SysprofDepthVisualizer, sysprof_depth_visualizer, SYSPROF, DEPTH_VISUALIZER, SysprofVisualizer) - -SysprofVisualizer *sysprof_depth_visualizer_new (SysprofDepthVisualizerMode mode); - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-details-page.c b/src/libsysprof-ui/sysprof-details-page.c deleted file mode 100644 index 0bcffa02..00000000 --- a/src/libsysprof-ui/sysprof-details-page.c +++ /dev/null @@ -1,325 +0,0 @@ -/* sysprof-details-page.c - * - * Copyright 2019 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 - */ - -#define G_LOG_DOMAIN "sysprof-details-page" - -#include "config.h" - -#include -#include - -#include "sysprof-mark-detail.h" -#include "sysprof-details-page.h" -#include "sysprof-ui-private.h" - -struct _SysprofDetailsPage -{ - GtkWidget parent_instance ; - - /* Template Objects */ - - GListStore *marks_store; - GtkSortListModel *mark_sort_model; - GtkLabel *counters; - GtkLabel *duration; - GtkLabel *filename; - GtkLabel *allocations; - GtkLabel *forks; - GtkLabel *marks; - GtkLabel *processes; - GtkLabel *samples; - GtkLabel *start_time; - GtkLabel *cpu_label; - - guint next_row; -}; - -G_DEFINE_TYPE (SysprofDetailsPage, sysprof_details_page, GTK_TYPE_WIDGET) - -#if GLIB_CHECK_VERSION(2, 56, 0) -# define _g_date_time_new_from_iso8601 g_date_time_new_from_iso8601 -#else -static GDateTime * -_g_date_time_new_from_iso8601 (const gchar *str, - GTimeZone *default_tz) -{ - GTimeVal tv; - - if (g_time_val_from_iso8601 (str, &tv)) - { - g_autoptr(GDateTime) dt = g_date_time_new_from_timeval_utc (&tv); - - if (default_tz) - return g_date_time_to_timezone (dt, default_tz); - else - return g_steal_pointer (&dt); - } - - return NULL; -} -#endif - -char * -format_time (GObject *unused, - gint64 time) -{ - return time ? _sysprof_format_duration (time) : g_strdup("—"); -} - -static void -sysprof_details_page_dispose (GObject *object) -{ - SysprofDetailsPage *self = (SysprofDetailsPage *)object; - GtkWidget *child; - - while ((child = gtk_widget_get_first_child (GTK_WIDGET (self)))) - gtk_widget_unparent (child); - - G_OBJECT_CLASS (sysprof_details_page_parent_class)->dispose (object); -} - -static void -sysprof_details_page_class_init (SysprofDetailsPageClass *klass) -{ - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->dispose = sysprof_details_page_dispose; - - g_type_ensure (SYSPROF_TYPE_MARK_DETAIL); - gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/sysprof/ui/sysprof-details-page.ui"); - gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT); - gtk_widget_class_bind_template_child (widget_class, SysprofDetailsPage, allocations); - gtk_widget_class_bind_template_child (widget_class, SysprofDetailsPage, counters); - gtk_widget_class_bind_template_child (widget_class, SysprofDetailsPage, cpu_label); - gtk_widget_class_bind_template_child (widget_class, SysprofDetailsPage, duration); - gtk_widget_class_bind_template_child (widget_class, SysprofDetailsPage, filename); - gtk_widget_class_bind_template_child (widget_class, SysprofDetailsPage, forks); - gtk_widget_class_bind_template_child (widget_class, SysprofDetailsPage, marks); - gtk_widget_class_bind_template_child (widget_class, SysprofDetailsPage, marks_store); - gtk_widget_class_bind_template_child (widget_class, SysprofDetailsPage, mark_sort_model); - gtk_widget_class_bind_template_child (widget_class, SysprofDetailsPage, processes); - gtk_widget_class_bind_template_child (widget_class, SysprofDetailsPage, samples); - gtk_widget_class_bind_template_child (widget_class, SysprofDetailsPage, start_time); - gtk_widget_class_bind_template_callback (widget_class, format_time); -} - -static void -sysprof_details_page_init (SysprofDetailsPage *self) -{ - gtk_widget_init_template (GTK_WIDGET (self)); - - self->next_row = 8; -} - -GtkWidget * -sysprof_details_page_new (void) -{ - return g_object_new (SYSPROF_TYPE_DETAILS_PAGE, NULL); -} - -static void -update_cpu_info_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - g_autoptr(SysprofDetailsPage) self = user_data; - g_autofree gchar *str = NULL; - - g_assert (SYSPROF_IS_DETAILS_PAGE (self)); - g_assert (G_IS_TASK (result)); - - if ((str = g_task_propagate_pointer (G_TASK (result), NULL))) - gtk_label_set_label (self->cpu_label, str); -} - -static bool -cpu_info_cb (const SysprofCaptureFrame *frame, - gpointer user_data) -{ - const SysprofCaptureFileChunk *fc = (gpointer)frame; - const gchar *endptr; - const gchar *line; - gchar **str = user_data; - - line = memmem ((gchar *)fc->data, fc->len, "model name", 10); - - if (!line) - return FALSE; - - endptr = (gchar *)fc->data + fc->len; - endptr = memchr (line, '\n', endptr - line); - - if (endptr) - { - gchar *tmp = *str = g_strndup (line, endptr - line); - for (; *tmp && *tmp != ':'; tmp++) - *tmp = ' '; - if (*tmp == ':') - *tmp = ' '; - g_strstrip (*str); - return FALSE; - } - - return TRUE; -} - -static void -sysprof_details_page_update_cpu_info_worker (GTask *task, - gpointer source_object, - gpointer task_data, - GCancellable *cancellable) -{ - SysprofCaptureCursor *cursor = task_data; - gchar *str = NULL; - - g_assert (G_IS_TASK (task)); - g_assert (cursor != NULL); - - sysprof_capture_cursor_foreach (cursor, cpu_info_cb, &str); - g_task_return_pointer (task, g_steal_pointer (&str), g_free); -} - -static void -sysprof_details_page_update_cpu_info (SysprofDetailsPage *self, - SysprofCaptureReader *reader) -{ - g_autoptr(SysprofCaptureCursor) cursor = NULL; - g_autoptr(GTask) task = NULL; - - g_assert (SYSPROF_IS_DETAILS_PAGE (self)); - g_assert (reader != NULL); - - cursor = sysprof_capture_cursor_new (reader); - sysprof_capture_cursor_add_condition (cursor, - sysprof_capture_condition_new_where_file ("/proc/cpuinfo")); - - task = g_task_new (NULL, NULL, update_cpu_info_cb, g_object_ref (self)); - g_task_set_task_data (task, - g_steal_pointer (&cursor), - (GDestroyNotify) sysprof_capture_cursor_unref); - g_task_run_in_thread (task, sysprof_details_page_update_cpu_info_worker); -} - -void -sysprof_details_page_set_reader (SysprofDetailsPage *self, - SysprofCaptureReader *reader) -{ - g_autoptr(GDateTime) dt = NULL; - g_autoptr(GDateTime) local = NULL; - g_autofree gchar *duration_str = NULL; - const gchar *filename; - const gchar *capture_at; - SysprofCaptureStat st_buf; - gint64 duration; - - g_return_if_fail (SYSPROF_IS_DETAILS_PAGE (self)); - g_return_if_fail (reader != NULL); - - sysprof_details_page_update_cpu_info (self, reader); - - if (!(filename = sysprof_capture_reader_get_filename (reader))) - filename = _("Memory Capture"); - - gtk_label_set_label (self->filename, filename); - - if ((capture_at = sysprof_capture_reader_get_time (reader)) && - (dt = _g_date_time_new_from_iso8601 (capture_at, NULL)) && - (local = g_date_time_to_local (dt))) - { - g_autofree gchar *str = g_date_time_format (local, "%x %X"); - gtk_label_set_label (self->start_time, str); - } - - duration = sysprof_capture_reader_get_end_time (reader) - - sysprof_capture_reader_get_start_time (reader); - duration_str = g_strdup_printf (_("%0.4lf seconds"), duration / (gdouble)SYSPROF_NSEC_PER_SEC); - gtk_label_set_label (self->duration, duration_str); - - if (sysprof_capture_reader_get_stat (reader, &st_buf)) - { -#define SET_FRAME_COUNT(field, TYPE) \ - G_STMT_START { \ - g_autofree gchar *str = NULL; \ - str = g_strdup_printf ("%"G_GSIZE_FORMAT, st_buf.frame_count[TYPE]); \ - gtk_label_set_label (self->field, str); \ - } G_STMT_END - - SET_FRAME_COUNT (samples, SYSPROF_CAPTURE_FRAME_SAMPLE); - SET_FRAME_COUNT (marks, SYSPROF_CAPTURE_FRAME_MARK); - SET_FRAME_COUNT (processes, SYSPROF_CAPTURE_FRAME_PROCESS); - SET_FRAME_COUNT (forks, SYSPROF_CAPTURE_FRAME_FORK); - SET_FRAME_COUNT (counters, SYSPROF_CAPTURE_FRAME_CTRSET); - SET_FRAME_COUNT (allocations, SYSPROF_CAPTURE_FRAME_ALLOCATION); - -#undef SET_FRAME_COUNT - } -} - -void -sysprof_details_page_add_mark (SysprofDetailsPage *self, - const gchar *mark, - gint64 min, - gint64 max, - gint64 avg, - gint64 hits) -{ - SysprofMarkDetail *detail; - - g_return_if_fail (SYSPROF_IS_DETAILS_PAGE (self)); - - detail = sysprof_mark_detail_new (mark, min, max, avg, hits); - - g_list_store_append (self->marks_store, detail); - /*gtk_list_store_append (self->marks_store, &iter); - gtk_list_store_set (self->marks_store, &iter, - 0, mark, - 1, min ? _sysprof_format_duration (min) : "—", - 2, max ? _sysprof_format_duration (max) : "—", - 3, avg ? _sysprof_format_duration (avg) : "—", - 4, hits, - 5, detail, - -1);*/ - g_object_unref (detail); -} - -void -sysprof_details_page_add_marks (SysprofDetailsPage *self, - const SysprofMarkStat *marks, - guint n_marks) -{ - g_return_if_fail (SYSPROF_IS_DETAILS_PAGE (self)); - g_return_if_fail (marks != NULL || n_marks == 0); - - if (marks == NULL || n_marks == 0) - return; - - /* Be reasonable */ - if (n_marks > 100) - n_marks = 100; - - for (guint i = 0; i < n_marks; i++) - sysprof_details_page_add_mark (self, - marks[i].name, - marks[i].min, - marks[i].max, - marks[i].avg, - marks[i].count); -} diff --git a/src/libsysprof-ui/sysprof-details-page.h b/src/libsysprof-ui/sysprof-details-page.h deleted file mode 100644 index 63b919fc..00000000 --- a/src/libsysprof-ui/sysprof-details-page.h +++ /dev/null @@ -1,57 +0,0 @@ -/* sysprof-details-page.h - * - * Copyright 2019 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 - -G_BEGIN_DECLS - -SYSPROF_ALIGNED_BEGIN (8) -typedef struct -{ - gchar name[152]; - guint64 count; - gint64 max; - gint64 min; - gint64 avg; - guint64 avg_count; -} SysprofMarkStat -SYSPROF_ALIGNED_END (8); - -#define SYSPROF_TYPE_DETAILS_PAGE (sysprof_details_page_get_type()) - -G_DECLARE_FINAL_TYPE (SysprofDetailsPage, sysprof_details_page, SYSPROF, DETAILS_PAGE, GtkWidget) - -GtkWidget *sysprof_details_page_new (void); -void sysprof_details_page_set_reader (SysprofDetailsPage *self, - SysprofCaptureReader *reader); -void sysprof_details_page_add_marks (SysprofDetailsPage *self, - const SysprofMarkStat *marks, - guint n_marks); -void sysprof_details_page_add_mark (SysprofDetailsPage *self, - const gchar *mark, - gint64 min, - gint64 max, - gint64 avg, - gint64 hits); - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-details-page.ui b/src/libsysprof-ui/sysprof-details-page.ui deleted file mode 100644 index e9de42de..00000000 --- a/src/libsysprof-ui/sysprof-details-page.ui +++ /dev/null @@ -1,361 +0,0 @@ - - - - - marks_store - true - - marks_view - - - - - diff --git a/src/libsysprof-ui/sysprof-diskstat-aid.c b/src/libsysprof-ui/sysprof-diskstat-aid.c deleted file mode 100644 index 676ea4a3..00000000 --- a/src/libsysprof-ui/sysprof-diskstat-aid.c +++ /dev/null @@ -1,269 +0,0 @@ -/* sysprof-diskstat-aid.c - * - * Copyright 2019 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 - */ - -#define G_LOG_DOMAIN "sysprof-diskstat-aid" - -#include "config.h" - -#include -#include - -#include "sysprof-color-cycle.h" -#include "sysprof-duplex-visualizer.h" -#include "sysprof-diskstat-aid.h" - -struct _SysprofDiskstatAid -{ - SysprofAid parent_instance; -}; - -typedef struct -{ - SysprofCaptureCursor *cursor; - SysprofDisplay *display; -} Present; - -G_DEFINE_TYPE (SysprofDiskstatAid, sysprof_diskstat_aid, SYSPROF_TYPE_AID) - -static void -present_free (gpointer data) -{ - Present *p = data; - - g_clear_pointer (&p->cursor, sysprof_capture_cursor_unref); - g_clear_object (&p->display); - g_slice_free (Present, p); -} - -/** - * sysprof_diskstat_aid_new: - * - * Create a new #SysprofDiskstatAid. - * - * Returns: (transfer full): a newly created #SysprofDiskstatAid - * - * Since: 3.34 - */ -SysprofAid * -sysprof_diskstat_aid_new (void) -{ - return g_object_new (SYSPROF_TYPE_DISKSTAT_AID, NULL); -} - -static void -sysprof_diskstat_aid_prepare (SysprofAid *self, - SysprofProfiler *profiler) -{ - g_autoptr(SysprofSource) source = NULL; - - g_assert (SYSPROF_IS_DISKSTAT_AID (self)); - g_assert (SYSPROF_IS_PROFILER (profiler)); - - source = sysprof_diskstat_source_new (); - sysprof_profiler_add_source (profiler, source); -} - -static bool -collect_diskstat_counters (const SysprofCaptureFrame *frame, - gpointer user_data) -{ - SysprofCaptureCounterDefine *def = (SysprofCaptureCounterDefine *)frame; - GArray *counters = user_data; - - g_assert (frame != NULL); - g_assert (frame->type == SYSPROF_CAPTURE_FRAME_CTRDEF); - g_assert (counters != NULL); - - for (guint i = 0; i < def->n_counters; i++) - { - const SysprofCaptureCounter *counter = &def->counters[i]; - - if (strcmp (counter->category, "Disk") == 0 && - (g_str_has_prefix (counter->name, "Total Reads") || - g_str_has_prefix (counter->name, "Total Writes"))) - g_array_append_vals (counters, counter, 1); - } - - return TRUE; -} - -static void -sysprof_diskstat_aid_present_worker (GTask *task, - gpointer source_object, - gpointer task_data, - GCancellable *cancellable) -{ - Present *present = task_data; - g_autoptr(GArray) counters = NULL; - - g_assert (G_IS_TASK (task)); - g_assert (SYSPROF_IS_DISKSTAT_AID (source_object)); - g_assert (present != NULL); - g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); - - counters = g_array_new (FALSE, FALSE, sizeof (SysprofCaptureCounter)); - sysprof_capture_cursor_foreach (present->cursor, collect_diskstat_counters, counters); - g_task_return_pointer (task, - g_steal_pointer (&counters), - (GDestroyNotify) g_array_unref); -} - -static guint -find_other_id (GArray *counters, - const gchar *name) -{ - g_autofree gchar *other = NULL; - - g_assert (counters); - g_assert (name != NULL); - g_assert (g_str_has_prefix (name, "Total Reads")); - - other = g_strdup_printf ("Total Writes%s", name + strlen ("Total Reads")); - - for (guint i = 0; i < counters->len; i++) - { - const SysprofCaptureCounter *c = &g_array_index (counters, SysprofCaptureCounter, i); - - if (g_str_equal (c->name, other)) - return c->id; - } - - return 0; -} - -static void -sysprof_diskstat_aid_present_async (SysprofAid *aid, - SysprofCaptureReader *reader, - SysprofDisplay *display, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - static const SysprofCaptureFrameType types[] = { SYSPROF_CAPTURE_FRAME_CTRDEF }; - g_autoptr(SysprofCaptureCondition) condition = NULL; - g_autoptr(SysprofCaptureCursor) cursor = NULL; - g_autoptr(GTask) task = NULL; - Present present; - - g_assert (SYSPROF_IS_DISKSTAT_AID (aid)); - g_assert (reader != NULL); - g_assert (SYSPROF_IS_DISPLAY (display)); - g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); - - condition = sysprof_capture_condition_new_where_type_in (1, types); - cursor = sysprof_capture_cursor_new (reader); - sysprof_capture_cursor_add_condition (cursor, g_steal_pointer (&condition)); - - present.cursor = g_steal_pointer (&cursor); - present.display = g_object_ref (display); - - task = g_task_new (aid, cancellable, callback, user_data); - g_task_set_source_tag (task, sysprof_diskstat_aid_present_async); - g_task_set_task_data (task, - g_slice_dup (Present, &present), - present_free); - g_task_run_in_thread (task, sysprof_diskstat_aid_present_worker); -} - -static gboolean -sysprof_diskstat_aid_present_finish (SysprofAid *aid, - GAsyncResult *result, - GError **error) -{ - g_autoptr(GArray) counters = NULL; - Present *present; - - g_assert (SYSPROF_IS_AID (aid)); - g_assert (G_IS_TASK (result)); - - present = g_task_get_task_data (G_TASK (result)); - - if ((counters = g_task_propagate_pointer (G_TASK (result), error))) - { - g_autoptr(SysprofColorCycle) cycle = sysprof_color_cycle_new (); - SysprofVisualizerGroup *group; - - group = g_object_new (SYSPROF_TYPE_VISUALIZER_GROUP, - "can-focus", TRUE, - "title", _("Disk"), - "visible", TRUE, - NULL); - - for (guint i = 0; i < counters->len; i++) - { - const SysprofCaptureCounter *ctr = &g_array_index (counters, SysprofCaptureCounter, i); - - if (g_str_has_prefix (ctr->name, "Total Reads")) - { - g_autofree gchar *title = NULL; - gboolean is_combined; - GtkWidget *row; - GdkRGBA rgba; - guint other_id; - - if (!(other_id = find_other_id (counters, ctr->name))) - continue; - - is_combined = g_str_equal (ctr->description, "Combined"); - - if (is_combined) - title = g_strdup ("Disk Reads/Writes (All)"); - else - title = g_strdup_printf ("Disk Reads/Writes%s", ctr->name + strlen ("Total Reads")); - - row = g_object_new (SYSPROF_TYPE_DUPLEX_VISUALIZER, - "title", title, - "height-request", 35, - "visible", is_combined, - NULL); - sysprof_color_cycle_next (cycle, &rgba); - sysprof_duplex_visualizer_set_counters (SYSPROF_DUPLEX_VISUALIZER (row), ctr->id, other_id); - sysprof_duplex_visualizer_set_colors (SYSPROF_DUPLEX_VISUALIZER (row), &rgba, &rgba); - sysprof_duplex_visualizer_set_labels (SYSPROF_DUPLEX_VISUALIZER (row), _("Reads"), _("Writes")); - sysprof_duplex_visualizer_set_use_diff (SYSPROF_DUPLEX_VISUALIZER (row), FALSE); - sysprof_visualizer_group_insert (group, SYSPROF_VISUALIZER (row), is_combined ? 0 : -1, !is_combined); - } - } - - if (counters->len > 0) - sysprof_display_add_group (present->display, group); - else - g_object_unref (g_object_ref_sink (group)); - } - - return counters != NULL; -} - -static void -sysprof_diskstat_aid_class_init (SysprofDiskstatAidClass *klass) -{ - SysprofAidClass *aid_class = SYSPROF_AID_CLASS (klass); - - aid_class->prepare = sysprof_diskstat_aid_prepare; - aid_class->present_async = sysprof_diskstat_aid_present_async; - aid_class->present_finish = sysprof_diskstat_aid_present_finish; -} - -static void -sysprof_diskstat_aid_init (SysprofDiskstatAid *self) -{ - sysprof_aid_set_display_name (SYSPROF_AID (self), _("Disk")); - sysprof_aid_set_icon_name (SYSPROF_AID (self), "drive-harddisk-system-symbolic"); -} diff --git a/src/libsysprof-ui/sysprof-diskstat-aid.h b/src/libsysprof-ui/sysprof-diskstat-aid.h deleted file mode 100644 index c2938a41..00000000 --- a/src/libsysprof-ui/sysprof-diskstat-aid.h +++ /dev/null @@ -1,33 +0,0 @@ -/* sysprof-diskstat-aid.h - * - * Copyright 2019 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-aid.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_DISKSTAT_AID (sysprof_diskstat_aid_get_type()) - -G_DECLARE_FINAL_TYPE (SysprofDiskstatAid, sysprof_diskstat_aid, SYSPROF, DISKSTAT_AID, SysprofAid) - -SysprofAid *sysprof_diskstat_aid_new (void); - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-display-private.h b/src/libsysprof-ui/sysprof-display-private.h deleted file mode 100644 index 2ca6ad64..00000000 --- a/src/libsysprof-ui/sysprof-display-private.h +++ /dev/null @@ -1,29 +0,0 @@ -/* sysprof-display-private.h - * - * Copyright 2021 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-display.h" - -G_BEGIN_DECLS - -void _sysprof_display_destroy (SysprofDisplay *self); - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-display.c b/src/libsysprof-ui/sysprof-display.c deleted file mode 100644 index 0c0ac27c..00000000 --- a/src/libsysprof-ui/sysprof-display.c +++ /dev/null @@ -1,1340 +0,0 @@ -/* sysprof-display.c - * - * Copyright 2019 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 - */ - -#define G_LOG_DOMAIN "sysprof-display" - -#include "config.h" - -#include - -#include "egg-paned-private.h" - -#include "sysprof-details-page.h" -#include "sysprof-display-private.h" -#include "sysprof-profiler-assistant.h" -#include "sysprof-failed-state-view.h" -#include "sysprof-recording-state-view.h" -#include "sysprof-theme-manager.h" -#include "sysprof-ui-private.h" -#include "sysprof-visualizers-frame.h" -#include "sysprof-visualizer-group-private.h" - -#include "sysprof-battery-aid.h" -#include "sysprof-callgraph-aid.h" -#include "sysprof-counters-aid.h" -#include "sysprof-cpu-aid.h" -#include "sysprof-diskstat-aid.h" -#include "sysprof-logs-aid.h" -#include "sysprof-marks-aid.h" -#include "sysprof-memory-aid.h" -#include "sysprof-memprof-aid.h" -#include "sysprof-netdev-aid.h" -#include "sysprof-rapl-aid.h" - -typedef enum -{ - SYSPROF_CAPTURE_FLAGS_CAN_REPLAY = 1 << 1, -} SysprofCaptureFlags; - -typedef struct -{ - SysprofCaptureReader *reader; - SysprofCaptureCondition *filter; - GFile *file; - SysprofProfiler *profiler; - GError *error; - - /* Template Widgets */ - SysprofVisualizersFrame *visualizers; - GtkStack *pages; - SysprofDetailsPage *details; - GtkStack *stack; - SysprofProfilerAssistant *assistant; - SysprofRecordingStateView *recording_view; - SysprofFailedStateView *failed_view; - - SysprofCaptureFlags flags; -} SysprofDisplayPrivate; - -G_DEFINE_TYPE_WITH_PRIVATE (SysprofDisplay, sysprof_display, GTK_TYPE_WIDGET) - -enum { - PROP_0, - PROP_CAN_REPLAY, - PROP_CAN_SAVE, - PROP_RECORDING, - PROP_TITLE, - PROP_VISIBLE_PAGE, - N_PROPS -}; - -static GParamSpec *properties [N_PROPS]; - -static void -update_title_child_property (SysprofDisplay *self) -{ - GtkWidget *parent; - - g_assert (SYSPROF_IS_DISPLAY (self)); - - if ((parent = gtk_widget_get_parent (GTK_WIDGET (self))) && GTK_IS_NOTEBOOK (parent)) - { - g_autofree gchar *title = sysprof_display_dup_title (self); - gtk_notebook_set_menu_label_text (GTK_NOTEBOOK (parent), GTK_WIDGET (self), title); - } - - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_TITLE]); -} - -static void -sysprof_display_profiler_failed_cb (SysprofDisplay *self, - const GError *error, - SysprofProfiler *profiler) -{ - SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self); - - g_assert (SYSPROF_IS_DISPLAY (self)); - g_assert (error != NULL); - g_assert (SYSPROF_IS_PROFILER (profiler)); - - g_clear_object (&priv->profiler); - - /* Save the error for future use */ - g_clear_error (&priv->error); - priv->error = g_error_copy (error); - - gtk_stack_set_visible_child (priv->stack, GTK_WIDGET (priv->failed_view)); - - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_RECORDING]); - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_TITLE]); - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CAN_REPLAY]); -} - -static void -sysprof_display_profiler_stopped_cb (SysprofDisplay *self, - SysprofProfiler *profiler) -{ - SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self); - SysprofCaptureWriter *writer; - - g_assert (SYSPROF_IS_DISPLAY (self)); - g_assert (SYSPROF_IS_PROFILER (profiler)); - - if ((writer = sysprof_profiler_get_writer (profiler))) - { - g_autoptr(SysprofCaptureReader) reader = NULL; - g_autoptr(GError) error = NULL; - - if (!(reader = sysprof_capture_writer_create_reader_with_error (writer, &error))) - { - g_warning ("Failed to create capture creader: %s\n", error->message); - gtk_stack_set_visible_child (priv->stack, GTK_WIDGET (priv->failed_view)); - goto notify; - } - - sysprof_display_load_async (self, - reader, - NULL, NULL, NULL); - gtk_stack_set_visible_child_name (priv->stack, "view"); - } - -notify: - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CAN_REPLAY]); - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CAN_SAVE]); - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_RECORDING]); - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_TITLE]); -} - -static void -sysprof_display_set_profiler (SysprofDisplay *self, - SysprofProfiler *profiler) -{ - SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self); - - g_assert (SYSPROF_IS_DISPLAY (self)); - g_assert (SYSPROF_IS_PROFILER (profiler)); - - if (g_set_object (&priv->profiler, profiler)) - { - sysprof_recording_state_view_set_profiler (priv->recording_view, profiler); - gtk_stack_set_visible_child (priv->stack, GTK_WIDGET (priv->recording_view)); - - g_signal_connect_object (profiler, - "stopped", - G_CALLBACK (sysprof_display_profiler_stopped_cb), - self, - G_CONNECT_SWAPPED); - - g_signal_connect_object (profiler, - "failed", - G_CALLBACK (sysprof_display_profiler_failed_cb), - self, - G_CONNECT_SWAPPED); - } - - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_RECORDING]); - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_TITLE]); -} - -static void -sysprof_display_start_recording_cb (SysprofDisplay *self, - SysprofProfiler *profiler, - SysprofProfilerAssistant *assistant) -{ - g_assert (SYSPROF_IS_DISPLAY (self)); - g_assert (SYSPROF_IS_PROFILER (profiler)); - g_assert (!assistant || SYSPROF_IS_PROFILER_ASSISTANT (assistant)); - g_assert (sysprof_display_is_empty (self)); - - sysprof_display_set_profiler (self, profiler); - sysprof_profiler_start (profiler); -} - -static gboolean -sysprof_display_get_is_recording (SysprofDisplay *self) -{ - SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self); - - g_assert (SYSPROF_IS_DISPLAY (self)); - - return GTK_WIDGET (priv->recording_view) == gtk_stack_get_visible_child (priv->stack); -} - -gchar * -sysprof_display_dup_title (SysprofDisplay *self) -{ - SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self); - - g_return_val_if_fail (SYSPROF_IS_DISPLAY (self), NULL); - - if (priv->error) - return g_strdup (_("Recording Failed")); - - if (priv->profiler != NULL) - { - if (sysprof_profiler_get_is_running (priv->profiler)) - return g_strdup (_("Recording…")); - } - - if (priv->file != NULL) - return g_file_get_basename (priv->file); - - if (priv->reader != NULL) - { - g_autoptr(GDateTime) dt = NULL; - const gchar *filename; - const gchar *capture_time; - - if ((filename = sysprof_capture_reader_get_filename (priv->reader))) - return g_path_get_basename (filename); - - /* This recording is not yet on disk, but the time it was recorded - * is much more useful than "New Recording". - */ - capture_time = sysprof_capture_reader_get_time (priv->reader); - - if ((dt = g_date_time_new_from_iso8601 (capture_time, NULL))) - { - g_autoptr(GDateTime) local = g_date_time_to_local (dt); - g_autofree gchar *formatted = NULL; - - if (local != NULL) - formatted = g_date_time_format (local, "%X"); - else - formatted = g_date_time_format (dt, "%X"); - - /* translators: %s is replaced with locale specific time of recording */ - return g_strdup_printf (_("Recording at %s"), formatted); - } - } - - return g_strdup (_("New Recording")); -} - -/** - * sysprof_display_new: - * - * Create a new #SysprofDisplay. - * - * Returns: (transfer full): a newly created #SysprofDisplay - */ -GtkWidget * -sysprof_display_new (void) -{ - return g_object_new (SYSPROF_TYPE_DISPLAY, NULL); -} - -static void -sysprof_display_notify_selection_cb (SysprofDisplay *self, - GParamSpec *pspec, - SysprofVisualizersFrame *visualizers) -{ - SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self); - SysprofSelection *selection; - - g_assert (SYSPROF_IS_DISPLAY (self)); - g_assert (SYSPROF_IS_VISUALIZERS_FRAME (visualizers)); - - g_clear_pointer (&priv->filter, sysprof_capture_condition_unref); - - if ((selection = sysprof_visualizers_frame_get_selection (visualizers))) - { - SysprofCaptureCondition *cond = NULL; - guint n_ranges = sysprof_selection_get_n_ranges (selection); - - for (guint i = 0; i < n_ranges; i++) - { - SysprofCaptureCondition *c; - gint64 begin, end; - - sysprof_selection_get_nth_range (selection, i, &begin, &end); - c = sysprof_capture_condition_new_where_time_between (begin, end); - - if (cond == NULL) - cond = c; - else - cond = sysprof_capture_condition_new_or (cond, c); - } - - priv->filter = cond; - - /* Opportunistically load pages */ - if (priv->reader != NULL) - { - for (GtkWidget *child = gtk_widget_get_first_child (GTK_WIDGET (priv->pages)); - child; - child = gtk_widget_get_next_sibling (child)) - { - if (SYSPROF_IS_PAGE (child)) - sysprof_page_load_async (SYSPROF_PAGE (child), - priv->reader, - selection, - priv->filter, - NULL, NULL, NULL); - } - } - } -} - -static void -change_page_cb (GSimpleAction *action, - GVariant *param, - gpointer user_data) -{ - SysprofDisplay *self = user_data; - SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self); - - g_assert (G_IS_SIMPLE_ACTION (action)); - g_assert (param != NULL); - - if (g_variant_is_of_type (param, G_VARIANT_TYPE_STRING)) - { - const gchar *str = g_variant_get_string (param, NULL); - - gtk_stack_set_visible_child_name (priv->pages, str); - - if (g_str_equal (str, "details")) - sysprof_visualizers_frame_unselect_row (priv->visualizers); - } -} - -static void -stop_recording_cb (GSimpleAction *action, - GVariant *param, - gpointer user_data) -{ - SysprofDisplay *self = user_data; - - g_assert (G_IS_SIMPLE_ACTION (action)); - g_assert (SYSPROF_IS_DISPLAY (self)); - - sysprof_display_stop_recording (self); -} - -static void -sysprof_display_dispose (GObject *object) -{ - SysprofDisplay *self = (SysprofDisplay *)object; - SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self); - - if (priv->stack) - { - gtk_widget_unparent (GTK_WIDGET (priv->stack)); - priv->stack = NULL; - } - - g_clear_pointer (&priv->reader, sysprof_capture_reader_unref); - g_clear_pointer (&priv->filter, sysprof_capture_condition_unref); - - G_OBJECT_CLASS (sysprof_display_parent_class)->dispose (object); -} - -static void -sysprof_display_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - SysprofDisplay *self = SYSPROF_DISPLAY (object); - - switch (prop_id) - { - case PROP_CAN_REPLAY: - g_value_set_boolean (value, sysprof_display_get_can_replay (self)); - break; - - case PROP_CAN_SAVE: - g_value_set_boolean (value, sysprof_display_get_can_save (self)); - break; - - case PROP_RECORDING: - g_value_set_boolean (value, sysprof_display_get_is_recording (self)); - break; - - case PROP_TITLE: - g_value_take_string (value, sysprof_display_dup_title (self)); - break; - - case PROP_VISIBLE_PAGE: - g_value_set_object (value, sysprof_display_get_visible_page (self)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_display_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - SysprofDisplay *self = SYSPROF_DISPLAY (object); - - switch (prop_id) - { - case PROP_VISIBLE_PAGE: - sysprof_display_set_visible_page (self, g_value_get_object (value)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_display_class_init (SysprofDisplayClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); - - object_class->dispose = sysprof_display_dispose; - object_class->get_property = sysprof_display_get_property; - object_class->set_property = sysprof_display_set_property; - - sysprof_theme_manager_register_resource (sysprof_theme_manager_get_default (), - NULL, - NULL, - "/org/gnome/sysprof/css/SysprofDisplay-shared.css"); - - gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/sysprof/ui/sysprof-display.ui"); - gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT); - gtk_widget_class_set_css_name (widget_class, "SysprofDisplay"); - gtk_widget_class_bind_template_child_private (widget_class, SysprofDisplay, assistant); - gtk_widget_class_bind_template_child_private (widget_class, SysprofDisplay, details); - gtk_widget_class_bind_template_child_private (widget_class, SysprofDisplay, failed_view); - gtk_widget_class_bind_template_child_private (widget_class, SysprofDisplay, pages); - gtk_widget_class_bind_template_child_private (widget_class, SysprofDisplay, recording_view); - gtk_widget_class_bind_template_child_private (widget_class, SysprofDisplay, stack); - gtk_widget_class_bind_template_child_private (widget_class, SysprofDisplay, visualizers); - - properties [PROP_CAN_REPLAY] = - g_param_spec_boolean ("can-replay", - "Can Replay", - "If the capture contains enough information to re-run the recording", - FALSE, - (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - - properties [PROP_CAN_SAVE] = - g_param_spec_boolean ("can-save", - "Can Save", - "If the display can save a recording", - FALSE, - (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - - properties [PROP_RECORDING] = - g_param_spec_boolean ("recording", - "Recording", - "If the display is in recording state", - FALSE, - (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - - properties [PROP_TITLE] = - g_param_spec_string ("title", - "Title", - "The title of the display", - NULL, - (G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); - - properties [PROP_VISIBLE_PAGE] = - g_param_spec_object ("visible-page", - "Visible Page", - "Visible Page", - SYSPROF_TYPE_PAGE, - (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_properties (object_class, N_PROPS, properties); - - g_type_ensure (EGG_TYPE_PANED); - g_type_ensure (SYSPROF_TYPE_DETAILS_PAGE); - g_type_ensure (SYSPROF_TYPE_FAILED_STATE_VIEW); - g_type_ensure (SYSPROF_TYPE_PROFILER_ASSISTANT); - g_type_ensure (SYSPROF_TYPE_RECORDING_STATE_VIEW); - g_type_ensure (SYSPROF_TYPE_VISUALIZERS_FRAME); -} - -static void -sysprof_display_init (SysprofDisplay *self) -{ - SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self); - g_autoptr(GSimpleActionGroup) group = g_simple_action_group_new (); - static GActionEntry entries[] = { - { "page", change_page_cb, "s" }, - { "stop-recording", stop_recording_cb }, - }; - g_autoptr(GPropertyAction) page = NULL; - - gtk_widget_init_template (GTK_WIDGET (self)); - - g_signal_connect_object (priv->assistant, - "start-recording", - G_CALLBACK (sysprof_display_start_recording_cb), - self, - G_CONNECT_SWAPPED); - - g_signal_connect_object (priv->visualizers, - "notify::selection", - G_CALLBACK (sysprof_display_notify_selection_cb), - self, - G_CONNECT_SWAPPED); - - page = g_property_action_new ("page", priv->pages, "visible-child-name"); - g_action_map_add_action_entries (G_ACTION_MAP (group), - entries, - G_N_ELEMENTS (entries), - self); - gtk_widget_insert_action_group (GTK_WIDGET (self), "display", G_ACTION_GROUP (group)); -} - -void -sysprof_display_add_group (SysprofDisplay *self, - SysprofVisualizerGroup *group) -{ - SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self); - - g_return_if_fail (SYSPROF_IS_DISPLAY (self)); - g_return_if_fail (SYSPROF_IS_VISUALIZER_GROUP (group)); - - if (priv->reader != NULL) - _sysprof_visualizer_group_set_reader (group, priv->reader); - - sysprof_visualizers_frame_add_group (priv->visualizers, group); -} - -void -sysprof_display_add_page (SysprofDisplay *self, - SysprofPage *page) -{ - SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self); - SysprofSelection *selection; - const gchar *title; - - g_return_if_fail (SYSPROF_IS_DISPLAY (self)); - g_return_if_fail (SYSPROF_IS_PAGE (page)); - - title = sysprof_page_get_title (page); - gtk_stack_add_titled (priv->pages, GTK_WIDGET (page), NULL, title); - - selection = sysprof_visualizers_frame_get_selection (priv->visualizers); - - sysprof_page_set_size_group (page, - sysprof_visualizers_frame_get_size_group (priv->visualizers)); - - sysprof_page_set_hadjustment (page, - sysprof_visualizers_frame_get_hadjustment (priv->visualizers)); - - if (priv->reader != NULL) - sysprof_page_load_async (page, - priv->reader, - selection, - priv->filter, - NULL, NULL, NULL); -} - -SysprofPage * -sysprof_display_get_visible_page (SysprofDisplay *self) -{ - SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self); - GtkWidget *visible_page; - - g_return_val_if_fail (SYSPROF_IS_DISPLAY (self), NULL); - - visible_page = gtk_stack_get_visible_child (priv->pages); - - if (SYSPROF_IS_PAGE (visible_page)) - return SYSPROF_PAGE (visible_page); - - return NULL; -} - -void -sysprof_display_set_visible_page (SysprofDisplay *self, - SysprofPage *page) -{ - SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self); - - g_return_if_fail (SYSPROF_IS_DISPLAY (self)); - g_return_if_fail (SYSPROF_IS_PAGE (page)); - - gtk_stack_set_visible_child (priv->pages, GTK_WIDGET (page)); -} - -static void -sysprof_display_present_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - SysprofAid *aid = (SysprofAid *)object; - g_autoptr(GTask) task = user_data; - g_autoptr(GError) error = NULL; - gatomicrefcount *n_active; - - g_assert (SYSPROF_IS_AID (aid)); - g_assert (G_IS_ASYNC_RESULT (result)); - g_assert (G_IS_TASK (task)); - - if (!sysprof_aid_present_finish (aid, result, &error)) - { - if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED)) - g_warning ("Failed to present aid %s: %s", G_OBJECT_TYPE_NAME (aid), error->message); - } - - n_active = g_task_get_task_data (task); - - if (g_atomic_ref_count_dec (n_active)) - g_task_return_boolean (task, TRUE); -} - -static void -sysprof_display_present_async (SysprofDisplay *self, - SysprofCaptureReader *reader, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - g_autoptr(GPtrArray) aids = NULL; - g_autoptr(GTask) task = NULL; - g_autofree gatomicrefcount *aids_len = NULL; - - g_return_if_fail (SYSPROF_IS_DISPLAY (self)); - g_return_if_fail (reader != NULL); - g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); - - aids = g_ptr_array_new_with_free_func (g_object_unref); - g_ptr_array_add (aids, sysprof_battery_aid_new ()); - g_ptr_array_add (aids, sysprof_counters_aid_new ()); - g_ptr_array_add (aids, sysprof_cpu_aid_new ()); - g_ptr_array_add (aids, sysprof_callgraph_aid_new ()); - g_ptr_array_add (aids, sysprof_diskstat_aid_new ()); - g_ptr_array_add (aids, sysprof_logs_aid_new ()); - g_ptr_array_add (aids, sysprof_marks_aid_new ()); - g_ptr_array_add (aids, sysprof_memory_aid_new ()); - g_ptr_array_add (aids, sysprof_memprof_aid_new ()); - g_ptr_array_add (aids, sysprof_netdev_aid_new ()); - g_ptr_array_add (aids, sysprof_rapl_aid_new ()); - - task = g_task_new (self, cancellable, callback, user_data); - g_task_set_source_tag (task, sysprof_display_present_async); - - if (aids->len == 0) - { - g_task_return_boolean (task, TRUE); - return; - } - - aids_len = g_new (gatomicrefcount, 1); - g_atomic_ref_count_init(aids_len); - *aids_len = aids->len; - - g_task_set_task_data (task, g_steal_pointer (&aids_len), g_free); - - for (guint i = 0; i < aids->len; i++) - { - SysprofAid *aid = g_ptr_array_index (aids, i); - - sysprof_aid_present_async (aid, - reader, - self, - cancellable, - sysprof_display_present_cb, - g_object_ref (task)); - } -} - -static gboolean -sysprof_display_present_finish (SysprofDisplay *self, - GAsyncResult *result, - GError **error) -{ - g_return_val_if_fail (SYSPROF_IS_DISPLAY (self), FALSE); - g_return_val_if_fail (G_IS_TASK (result), FALSE); - - return g_task_propagate_boolean (G_TASK (result), error); -} - -static void -sysprof_display_scan_worker (GTask *task, - gpointer source_object, - gpointer task_data, - GCancellable *cancellable) -{ - SysprofDisplay *self = source_object; - SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self); - SysprofCaptureReader *reader = task_data; - g_autoptr(GHashTable) mark_stats = NULL; - g_autoptr(GArray) marks = NULL; - SysprofCaptureFrame frame; - SysprofCaptureStat st = {{0}}; - SysprofCaptureFlags flags = 0; - - g_assert (G_IS_TASK (task)); - g_assert (SYSPROF_IS_DISPLAY (self)); - g_assert (reader != NULL); - g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); - - /* Scan the reader until the end so that we know we have gotten - * all of the timing data loaded into the underlying reader. - */ - - mark_stats = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - marks = g_array_new (FALSE, FALSE, sizeof (SysprofMarkStat)); - - while (sysprof_capture_reader_peek_frame (reader, &frame)) - { - if (frame.type < G_N_ELEMENTS (st.frame_count)) - st.frame_count[frame.type]++; - - if (frame.type == SYSPROF_CAPTURE_FRAME_METADATA) - { - const SysprofCaptureMetadata *meta; - - if ((meta = sysprof_capture_reader_read_metadata (reader))) - { - if (g_strcmp0 (meta->id, "local-profiler") == 0) - flags |= SYSPROF_CAPTURE_FLAGS_CAN_REPLAY; - } - } - else if (frame.type == SYSPROF_CAPTURE_FRAME_MARK) - { - const SysprofCaptureMark *mark; - - if ((mark = sysprof_capture_reader_read_mark (reader))) - { - SysprofMarkStat *mstat; - gchar name[152]; - gpointer idx; - - g_snprintf (name, sizeof name, "%s:%s", mark->group, mark->name); - - if (!(idx = g_hash_table_lookup (mark_stats, name))) - { - SysprofMarkStat empty = {{0}}; - - g_strlcpy (empty.name, name, sizeof empty.name); - g_array_append_val (marks, empty); - idx = GUINT_TO_POINTER (marks->len); - g_hash_table_insert (mark_stats, g_strdup (name), idx); - } - - mstat = &g_array_index (marks, SysprofMarkStat, GPOINTER_TO_UINT (idx) - 1); - - if (mark->duration > 0) - { - if (mstat->min == 0 || mark->duration < mstat->min) - mstat->min = mark->duration; - } - - if (mark->duration > mstat->max) - mstat->max = mark->duration; - - if (mark->duration > 0) - { - mstat->avg += mark->duration; - mstat->avg_count++; - } - - mstat->count++; - } - } - else - { - sysprof_capture_reader_skip (reader); - } - } - - { - GHashTableIter iter; - gpointer k,v; - - g_hash_table_iter_init (&iter, mark_stats); - while (g_hash_table_iter_next (&iter, &k, &v)) - { - guint idx = GPOINTER_TO_UINT (v) - 1; - SysprofMarkStat *mstat = &g_array_index (marks, SysprofMarkStat, idx); - - if (mstat->avg_count > 0 && mstat->avg > 0) - mstat->avg /= mstat->avg_count; - -#if 0 - g_print ("%s: count=%ld avg=%ld min=%ld max=%ld\n", - (gchar*)k, - ((SysprofMarkStat *)v)->count, - ((SysprofMarkStat *)v)->avg, - ((SysprofMarkStat *)v)->min, - ((SysprofMarkStat *)v)->max); -#endif - } - } - - g_object_set_data_full (G_OBJECT (task), - "MARK_STAT", - g_steal_pointer (&marks), - (GDestroyNotify) g_array_unref); - - g_atomic_int_set (&priv->flags, flags); - sysprof_capture_reader_reset (reader); - sysprof_capture_reader_set_stat (reader, &st); - - g_task_return_boolean (task, TRUE); -} - -static void -sysprof_display_scan_async (SysprofDisplay *self, - SysprofCaptureReader *reader, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - g_autoptr(GTask) task = NULL; - - g_return_if_fail (SYSPROF_IS_DISPLAY (self)); - g_return_if_fail (reader != NULL); - g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); - - task = g_task_new (self, cancellable, callback, user_data); - g_task_set_source_tag (task, sysprof_display_scan_async); - g_task_set_task_data (task, - sysprof_capture_reader_ref (reader), - (GDestroyNotify) sysprof_capture_reader_unref); - g_task_run_in_thread (task, sysprof_display_scan_worker); -} - -static gboolean -sysprof_display_scan_finish (SysprofDisplay *self, - GAsyncResult *result, - GError **error) -{ - SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self); - GArray *marks; - - g_return_val_if_fail (SYSPROF_IS_DISPLAY (self), FALSE); - g_return_val_if_fail (G_IS_TASK (result), FALSE); - - if ((marks = g_object_get_data (G_OBJECT (result), "MARK_STAT"))) - sysprof_details_page_add_marks (priv->details, - (const SysprofMarkStat *)(gpointer)marks->data, - marks->len); - - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CAN_REPLAY]); - - return g_task_propagate_boolean (G_TASK (result), error); -} - -static void -sysprof_display_load_present_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - SysprofDisplay *self = (SysprofDisplay *)object; - g_autoptr(GError) error = NULL; - g_autoptr(GTask) task = user_data; - - g_assert (SYSPROF_IS_DISPLAY (self)); - g_assert (G_IS_ASYNC_RESULT (result)); - g_assert (G_IS_TASK (task)); - - if (!sysprof_display_present_finish (self, result, &error)) - g_warning ("Error presenting: %s", error->message); - - g_task_return_boolean (task, TRUE); -} - -static void -sysprof_display_load_frame_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - SysprofVisualizersFrame *frame = (SysprofVisualizersFrame *)object; - g_autoptr(GError) error = NULL; - g_autoptr(GTask) task = user_data; - SysprofCaptureReader *reader; - SysprofDisplay *self; - GCancellable *cancellable; - - g_assert (SYSPROF_IS_VISUALIZERS_FRAME (frame)); - g_assert (G_IS_ASYNC_RESULT (result)); - g_assert (G_IS_TASK (task)); - - self = g_task_get_source_object (task); - reader = g_task_get_task_data (task); - cancellable = g_task_get_cancellable (task); - - if (!sysprof_visualizers_frame_load_finish (frame, result, &error)) - g_task_return_error (task, g_steal_pointer (&error)); - else - sysprof_display_present_async (self, - reader, - cancellable, - sysprof_display_load_present_cb, - g_steal_pointer (&task)); -} - -static void -sysprof_display_load_scan_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - SysprofDisplay *self = (SysprofDisplay *)object; - SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self); - g_autoptr(GTask) task = user_data; - g_autoptr(GError) error = NULL; - SysprofCaptureReader *reader; - SysprofSelection *selection; - GCancellable *cancellable; - - g_assert (SYSPROF_IS_DISPLAY (self)); - g_assert (G_IS_ASYNC_RESULT (result)); - g_assert (G_IS_TASK (task)); - - reader = g_task_get_task_data (task); - cancellable = g_task_get_cancellable (task); - - if (!sysprof_display_scan_finish (self, result, &error)) - g_task_return_error (task, g_steal_pointer (&error)); - else - sysprof_visualizers_frame_load_async (priv->visualizers, - reader, - cancellable, - sysprof_display_load_frame_cb, - g_steal_pointer (&task)); - - selection = sysprof_visualizers_frame_get_selection (priv->visualizers); - - sysprof_details_page_set_reader (priv->details, reader); - - /* Opportunistically load pages */ - for (GtkWidget *child = gtk_widget_get_first_child (GTK_WIDGET (priv->pages)); - child; - child = gtk_widget_get_next_sibling (child)) - { - if (SYSPROF_IS_PAGE (child)) - sysprof_page_load_async (SYSPROF_PAGE (child), - reader, - selection, - priv->filter, - NULL, NULL, NULL); - } - - gtk_stack_set_visible_child_name (priv->stack, "view"); -} - -void -sysprof_display_load_async (SysprofDisplay *self, - SysprofCaptureReader *reader, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self); - g_autoptr(GTask) task = NULL; - - g_return_if_fail (SYSPROF_IS_DISPLAY (self)); - g_return_if_fail (reader != NULL); - g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); - - if (priv->reader != reader) - { - g_clear_pointer (&priv->reader, sysprof_capture_reader_unref); - priv->reader = sysprof_capture_reader_ref (reader); - } - - task = g_task_new (self, cancellable, callback, user_data); - g_task_set_source_tag (task, sysprof_display_load_async); - g_task_set_task_data (task, - sysprof_capture_reader_ref (reader), - (GDestroyNotify) sysprof_capture_reader_unref); - - /* First scan the reader for any sort of data we care about before - * we notify aids to load content. That allows us to ensure we have - * proper timing data for the consumers. - */ - sysprof_display_scan_async (self, - reader, - cancellable, - sysprof_display_load_scan_cb, - g_steal_pointer (&task)); -} - -gboolean -sysprof_display_load_finish (SysprofDisplay *self, - GAsyncResult *result, - GError **error) -{ - g_return_val_if_fail (SYSPROF_IS_DISPLAY (self), FALSE); - g_return_val_if_fail (G_IS_TASK (result), FALSE); - - return g_task_propagate_boolean (G_TASK (result), error); -} - -SysprofZoomManager * -sysprof_display_get_zoom_manager (SysprofDisplay *self) -{ - SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self); - - g_return_val_if_fail (SYSPROF_IS_DISPLAY (self), NULL); - - return sysprof_visualizers_frame_get_zoom_manager (priv->visualizers); -} - -/** - * sysprof_display_is_empty: - * - * Checks if any content is or will be loaded into @self. - * - * Returns: %TRUE if the tab is unperterbed. - * - * Since: 3.34 - */ -gboolean -sysprof_display_is_empty (SysprofDisplay *self) -{ - SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self); - - g_return_val_if_fail (SYSPROF_IS_DISPLAY (self), FALSE); - - return priv->file == NULL && - priv->profiler == NULL && - gtk_stack_get_visible_child (priv->stack) == GTK_WIDGET (priv->assistant) && - NULL == priv->reader; -} - -void -sysprof_display_open (SysprofDisplay *self, - GFile *file) -{ - SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self); - g_autoptr(SysprofCaptureReader) reader = NULL; - g_autoptr(GError) error = NULL; - g_autofree gchar *path = NULL; - - g_return_if_fail (SYSPROF_IS_DISPLAY (self)); - g_return_if_fail (G_IS_FILE (file)); - g_return_if_fail (g_file_is_native (file)); - g_return_if_fail (sysprof_display_is_empty (self)); - - path = g_file_get_path (file); - - /* If the file is executable, just set the path to the binary - * in the profiler assistant. - */ - if (g_file_test (path, G_FILE_TEST_IS_EXECUTABLE)) - { - sysprof_profiler_assistant_set_executable (priv->assistant, path); - return; - } - - g_set_object (&priv->file, file); - - if (!(reader = sysprof_capture_reader_new_with_error (path, &error))) - { - GtkWidget *dialog; - GtkWidget *window; - - g_warning ("Failed to open capture: %s", error->message); - - window = gtk_widget_get_ancestor (GTK_WIDGET (self), GTK_TYPE_WINDOW); - dialog = gtk_message_dialog_new (NULL, - GTK_DIALOG_MODAL, - GTK_MESSAGE_WARNING, - GTK_BUTTONS_CLOSE, - "%s", - _("The recording could not be opened")); - gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), - "%s", - error->message); - g_signal_connect (dialog, - "response", - G_CALLBACK (gtk_window_destroy), - NULL); - gtk_window_set_modal (GTK_WINDOW (dialog), TRUE); - gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (window)); - gtk_window_present (GTK_WINDOW (dialog)); - - _sysprof_display_destroy (self); - - return; - } - - /* Jump right to the "view" page to avoid a quick view of the - * assistants page when loading. - */ - if (g_strcmp0 ("assistant", gtk_stack_get_visible_child_name (priv->stack)) == 0) - gtk_stack_set_visible_child_name (priv->stack, "view"); - - sysprof_display_load_async (self, reader, NULL, NULL, NULL); - update_title_child_property (self); -} - -gboolean -sysprof_display_get_can_save (SysprofDisplay *self) -{ - SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self); - - g_return_val_if_fail (SYSPROF_IS_DISPLAY (self), FALSE); - - return priv->reader != NULL; -} - -void -sysprof_display_stop_recording (SysprofDisplay *self) -{ - SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self); - - g_return_if_fail (SYSPROF_IS_DISPLAY (self)); - - if (priv->profiler != NULL) - sysprof_profiler_stop (priv->profiler); -} - -void -_sysprof_display_focus_record (SysprofDisplay *self) -{ - SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self); - - g_return_if_fail (SYSPROF_IS_DISPLAY (self)); - - _sysprof_profiler_assistant_focus_record (priv->assistant); -} - -gboolean -sysprof_display_get_can_replay (SysprofDisplay *self) -{ - SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self); - - g_return_val_if_fail (SYSPROF_IS_DISPLAY (self), FALSE); - - return !sysprof_display_is_empty (self) && - priv->reader != NULL && - !!(priv->flags & SYSPROF_CAPTURE_FLAGS_CAN_REPLAY); -} - -/** - * sysprof_display_replay: - * @self: a #SysprofDisplay - * - * Gets a new display that will re-run the previous recording that is being - * viewed. This requires a capture file which contains enough information to - * replay the operation. - * - * Returns: (nullable) (transfer full): a #SysprofDisplay or %NULL - * - * Since: 3.34 - */ -SysprofDisplay * -sysprof_display_replay (SysprofDisplay *self) -{ - SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self); - g_autoptr(SysprofProfiler) profiler = NULL; - SysprofDisplay *copy; - - g_return_val_if_fail (SYSPROF_IS_DISPLAY (self), NULL); - g_return_val_if_fail (priv->reader != NULL, NULL); - - profiler = sysprof_local_profiler_new_replay (priv->reader); - g_return_val_if_fail (profiler != NULL, NULL); - g_return_val_if_fail (SYSPROF_IS_LOCAL_PROFILER (profiler), NULL); - - copy = g_object_new (SYSPROF_TYPE_DISPLAY, NULL); - sysprof_display_set_profiler (copy, profiler); - sysprof_profiler_start (profiler); - - return g_steal_pointer (©); -} - -GtkWidget * -sysprof_display_new_for_profiler (SysprofProfiler *profiler) -{ - SysprofDisplay *self; - - g_return_val_if_fail (SYSPROF_IS_PROFILER (profiler), NULL); - - self = g_object_new (SYSPROF_TYPE_DISPLAY, NULL); - sysprof_display_set_profiler (self, profiler); - - return GTK_WIDGET (g_steal_pointer (&self)); -} - -static void -on_save_response_cb (SysprofDisplay *self, - int res, - GtkFileChooserNative *chooser) -{ - SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self); - g_autoptr(GFile) file = NULL; - - g_assert (SYSPROF_IS_DISPLAY (self)); - g_assert (GTK_IS_FILE_CHOOSER_NATIVE (chooser)); - - switch (res) - { - case GTK_RESPONSE_ACCEPT: - file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (chooser)); - - if (g_file_is_native (file)) - { - g_autofree gchar *path = g_file_get_path (file); - g_autoptr(GError) error = NULL; - - if (!sysprof_capture_reader_save_as_with_error (priv->reader, path, &error)) - { - GtkWidget *msg; - GtkNative *root; - - root = gtk_widget_get_native (GTK_WIDGET (self)); - msg = gtk_message_dialog_new (GTK_WINDOW (root), - GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_USE_HEADER_BAR, - GTK_MESSAGE_ERROR, - GTK_BUTTONS_CLOSE, - _("Failed to save recording: %s"), - error->message); - gtk_window_present (GTK_WINDOW (msg)); - g_signal_connect (msg, "response", G_CALLBACK (gtk_window_destroy), NULL); - } - } - else - { - g_autofree char *uri = g_file_get_uri (file); - g_warning ("%s is not native, cannot open", uri); - } - - break; - - default: - break; - } - - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_TITLE]); - gtk_native_dialog_destroy (GTK_NATIVE_DIALOG (chooser)); -} - -void -sysprof_display_save (SysprofDisplay *self) -{ - SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self); - GtkFileChooserNative *native; - GtkNative *root; - - g_return_if_fail (SYSPROF_IS_DISPLAY (self)); - g_return_if_fail (priv->reader != NULL); - - root = gtk_widget_get_native (GTK_WIDGET (self)); - native = gtk_file_chooser_native_new (_("Save Recording"), - GTK_WINDOW (root), - GTK_FILE_CHOOSER_ACTION_SAVE, - _("Save"), - _("Cancel")); - gtk_file_chooser_set_create_folders (GTK_FILE_CHOOSER (native), TRUE); - gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (native), "capture.syscap"); - - g_signal_connect_object (native, - "response", - G_CALLBACK (on_save_response_cb), - self, - G_CONNECT_SWAPPED); - - gtk_native_dialog_show (GTK_NATIVE_DIALOG (native)); -} - -void -_sysprof_display_reload_page (SysprofDisplay *self, - SysprofPage *page) -{ - SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self); - SysprofSelection *selection; - - g_return_if_fail (SYSPROF_IS_DISPLAY (self)); - g_return_if_fail (SYSPROF_IS_PAGE (page)); - g_return_if_fail (priv->reader != NULL); - - selection = sysprof_visualizers_frame_get_selection (priv->visualizers); - - sysprof_page_load_async (page, - priv->reader, - selection, - priv->filter, - NULL, NULL, NULL); -} - -void -sysprof_display_add_to_selection (SysprofDisplay *self, - gint64 begin_time, - gint64 end_time) -{ - SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self); - SysprofSelection *selection; - - g_return_if_fail (SYSPROF_IS_DISPLAY (self)); - - selection = sysprof_visualizers_frame_get_selection (priv->visualizers); - sysprof_selection_select_range (selection, begin_time, end_time); -} - -void -_sysprof_display_destroy (SysprofDisplay *self) -{ - GtkWidget *parent; - - g_return_if_fail (SYSPROF_IS_DISPLAY (self)); - - if ((parent = gtk_widget_get_ancestor (GTK_WIDGET (self), GTK_TYPE_NOTEBOOK))) - gtk_notebook_remove_page (GTK_NOTEBOOK (parent), - gtk_notebook_page_num (GTK_NOTEBOOK (parent), GTK_WIDGET (self))); -} diff --git a/src/libsysprof-ui/sysprof-display.h b/src/libsysprof-ui/sysprof-display.h deleted file mode 100644 index 16cd9f55..00000000 --- a/src/libsysprof-ui/sysprof-display.h +++ /dev/null @@ -1,96 +0,0 @@ -/* sysprof-display.h - * - * Copyright 2019 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 - -#include "sysprof-page.h" -#include "sysprof-visualizer-group.h" -#include "sysprof-zoom-manager.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_DISPLAY (sysprof_display_get_type()) - -SYSPROF_AVAILABLE_IN_ALL -G_DECLARE_DERIVABLE_TYPE (SysprofDisplay, sysprof_display, SYSPROF, DISPLAY, GtkWidget) - -struct _SysprofDisplayClass -{ - GtkWidgetClass parent_class; - - /*< private >*/ - gpointer _reserved[16]; -}; - -SYSPROF_AVAILABLE_IN_ALL -GtkWidget *sysprof_display_new (void); -SYSPROF_AVAILABLE_IN_ALL -GtkWidget *sysprof_display_new_for_profiler (SysprofProfiler *profiler); -SYSPROF_AVAILABLE_IN_ALL -char *sysprof_display_dup_title (SysprofDisplay *self); -SYSPROF_AVAILABLE_IN_ALL -SysprofProfiler *sysprof_display_get_profiler (SysprofDisplay *self); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_display_add_group (SysprofDisplay *self, - SysprofVisualizerGroup *group); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_display_add_page (SysprofDisplay *self, - SysprofPage *page); -SYSPROF_AVAILABLE_IN_ALL -SysprofPage *sysprof_display_get_visible_page (SysprofDisplay *self); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_display_set_visible_page (SysprofDisplay *self, - SysprofPage *page); -SYSPROF_AVAILABLE_IN_ALL -SysprofZoomManager *sysprof_display_get_zoom_manager (SysprofDisplay *self); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_display_load_async (SysprofDisplay *self, - SysprofCaptureReader *reader, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -SYSPROF_AVAILABLE_IN_ALL -gboolean sysprof_display_load_finish (SysprofDisplay *self, - GAsyncResult *result, - GError **error); -SYSPROF_AVAILABLE_IN_ALL -gboolean sysprof_display_is_empty (SysprofDisplay *self); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_display_open (SysprofDisplay *self, - GFile *file); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_display_save (SysprofDisplay *self); -SYSPROF_AVAILABLE_IN_ALL -gboolean sysprof_display_get_can_save (SysprofDisplay *self); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_display_stop_recording (SysprofDisplay *self); -SYSPROF_AVAILABLE_IN_ALL -gboolean sysprof_display_get_can_replay (SysprofDisplay *self); -SYSPROF_AVAILABLE_IN_ALL -SysprofDisplay *sysprof_display_replay (SysprofDisplay *self); -SYSPROF_AVAILABLE_IN_3_38 -void sysprof_display_add_to_selection (SysprofDisplay *self, - gint64 begin_time, - gint64 end_time); - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-display.ui b/src/libsysprof-ui/sysprof-display.ui deleted file mode 100644 index 61fceb7a..00000000 --- a/src/libsysprof-ui/sysprof-display.ui +++ /dev/null @@ -1,71 +0,0 @@ - - - - - diff --git a/src/libsysprof-ui/sysprof-duplex-visualizer.c b/src/libsysprof-ui/sysprof-duplex-visualizer.c deleted file mode 100644 index e9289a62..00000000 --- a/src/libsysprof-ui/sysprof-duplex-visualizer.c +++ /dev/null @@ -1,633 +0,0 @@ -/* sysprof-duplex-visualizer.c - * - * Copyright 2019 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 - */ - -#define G_LOG_DOMAIN "sysprof-duplex-visualizer" - -#include "config.h" - -#include "pointcache.h" -#include "sysprof-duplex-visualizer.h" - -#define LABEL_HEIGHT_PX 10 - -struct _SysprofDuplexVisualizer -{ - SysprofVisualizer parent_instance; - - gint64 begin_time; - gint64 duration; - - guint rx_counter; - guint tx_counter; - - GdkRGBA rx_rgba; - GdkRGBA tx_rgba; - - gchar *rx_label; - gchar *tx_label; - - PointCache *cache; - - guint rx_rgba_set : 1; - guint tx_rgba_set : 1; - guint use_diff : 1; -}; - -typedef struct -{ - PointCache *cache; - - gint64 begin_time; - gint64 duration; - - gint64 max_change; - - /* Last value to convert to rate of change */ - gint64 last_rx_val; - gint64 last_tx_val; - - /* Counter IDs */ - guint rx; - guint tx; - - /* Do we need to subtract previous value */ - guint use_diff : 1; -} Collect; - -G_DEFINE_TYPE (SysprofDuplexVisualizer, sysprof_duplex_visualizer, SYSPROF_TYPE_VISUALIZER) - -static bool -collect_ranges_cb (const SysprofCaptureFrame *frame, - gpointer data) -{ - Collect *state = data; - - g_assert (frame != NULL); - g_assert (state != NULL); - g_assert (state->cache != NULL); - - if (frame->type == SYSPROF_CAPTURE_FRAME_CTRSET) - { - const SysprofCaptureCounterSet *set = (gconstpointer)frame; - - for (guint i = 0; i < set->n_values; i++) - { - const SysprofCaptureCounterValues *values = &set->values[i]; - - for (guint j = 0; j < G_N_ELEMENTS (values->ids); j++) - { - gint64 v64 = values->values[j].v64; - guint id = values->ids[j]; - gint64 max_change = 0; - - if (id == 0) - break; - - if (id == state->rx) - { - if (state->last_rx_val != G_MININT64) - max_change = v64 - state->last_rx_val; - state->last_rx_val = v64; - } - else if (id == state->tx) - { - if (state->last_tx_val != G_MININT64) - max_change = v64 - state->last_tx_val; - state->last_tx_val = v64; - } - else - { - continue; - } - - if (max_change > state->max_change) - state->max_change = max_change; - } - } - } - - return TRUE; -} - -static bool -collect_values_cb (const SysprofCaptureFrame *frame, - gpointer data) -{ - Collect *state = data; - - g_assert (frame != NULL); - g_assert (state != NULL); - g_assert (state->cache != NULL); - - if (frame->type == SYSPROF_CAPTURE_FRAME_CTRSET) - { - const SysprofCaptureCounterSet *set = (gconstpointer)frame; - gdouble x = (frame->time - state->begin_time) / (gdouble)state->duration; - - for (guint i = 0; i < set->n_values; i++) - { - const SysprofCaptureCounterValues *values = &set->values[i]; - - for (guint j = 0; j < G_N_ELEMENTS (values->ids); j++) - { - gint64 v64 = values->values[j].v64; - guint id = values->ids[j]; - gint64 val = v64; - gdouble y = 0.5; - - if (id == 0) - break; - - if (id == state->rx) - { - if (state->use_diff) - { - if (state->last_rx_val == G_MININT64) - val = 0; - else - val -= state->last_rx_val; - } - - /* RX goes upward from half point */ - if (state->max_change != 0) - y += (gdouble)val / (gdouble)state->max_change / 2.0; - - state->last_rx_val = v64; - } - else if (id == state->tx) - { - if (state->use_diff) - { - if (state->last_tx_val == G_MININT64) - val = 0; - else - val -= state->last_tx_val; - } - - /* TX goes downward from half point */ - if (state->max_change != 0) - y -= (gdouble)val / (gdouble)state->max_change / 2.0; - - state->last_tx_val = v64; - } - else - { - continue; - } - - point_cache_add_point_to_set (state->cache, id, x, y); - } - } - } - - return TRUE; -} - -static void -sysprof_duplex_visualizer_worker (GTask *task, - gpointer source_object, - gpointer task_data, - GCancellable *cancellable) -{ - SysprofCaptureCursor *cursor = task_data; - SysprofDuplexVisualizer *self = source_object; - Collect state = {0}; - - g_assert (G_IS_TASK (task)); - g_assert (SYSPROF_IS_DUPLEX_VISUALIZER (self)); - g_assert (cursor != NULL); - g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); - - state.cache = point_cache_new (); - state.begin_time = self->begin_time; - state.duration = self->duration; - state.rx = g_atomic_int_get (&self->rx_counter); - state.tx = g_atomic_int_get (&self->tx_counter); - state.last_rx_val = G_MININT64; - state.last_tx_val = G_MININT64; - state.max_change = 0; - state.use_diff = self->use_diff; - - point_cache_add_set (state.cache, state.rx); - point_cache_add_set (state.cache, state.tx); - - sysprof_capture_cursor_foreach (cursor, collect_ranges_cb, &state); - sysprof_capture_cursor_reset (cursor); - - /* Give just a bit of overhead */ - state.max_change *= 1.1; - - /* Reset for calculations */ - state.last_rx_val = G_MININT64; - state.last_tx_val = G_MININT64; - - sysprof_capture_cursor_foreach (cursor, collect_values_cb, &state); - - g_task_return_pointer (task, - g_steal_pointer (&state.cache), - (GDestroyNotify) point_cache_unref); -} - -static void -load_data_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - SysprofDuplexVisualizer *self = (SysprofDuplexVisualizer *)object; - g_autoptr(PointCache) pc = NULL; - - g_assert (SYSPROF_IS_DUPLEX_VISUALIZER (self)); - g_assert (G_IS_TASK (result)); - - if ((pc = g_task_propagate_pointer (G_TASK (result), NULL))) - { - g_clear_pointer (&self->cache, point_cache_unref); - self->cache = g_steal_pointer (&pc); - gtk_widget_queue_draw (GTK_WIDGET (self)); - } -} - -static void -sysprof_duplex_visualizer_set_reader (SysprofVisualizer *visualizer, - SysprofCaptureReader *reader) -{ - SysprofDuplexVisualizer *self = (SysprofDuplexVisualizer *)visualizer; - g_autoptr(SysprofCaptureCursor) cursor = NULL; - g_autoptr(GTask) task = NULL; - SysprofCaptureCondition *c; - guint counters[2]; - - g_assert (SYSPROF_IS_DUPLEX_VISUALIZER (self)); - g_assert (reader != NULL); - - self->begin_time = sysprof_capture_reader_get_start_time (reader); - self->duration = sysprof_capture_reader_get_end_time (reader) - - sysprof_capture_reader_get_start_time (reader); - - counters[0] = self->rx_counter; - counters[1] = self->tx_counter; - - cursor = sysprof_capture_cursor_new (reader); - c = sysprof_capture_condition_new_where_counter_in (G_N_ELEMENTS (counters), counters); - sysprof_capture_cursor_add_condition (cursor, g_steal_pointer (&c)); - - task = g_task_new (self, NULL, load_data_cb, NULL); - g_task_set_source_tag (task, sysprof_duplex_visualizer_set_reader); - g_task_set_task_data (task, - g_steal_pointer (&cursor), - (GDestroyNotify)sysprof_capture_cursor_unref); - g_task_run_in_thread (task, sysprof_duplex_visualizer_worker); -} - -static void -sysprof_duplex_visualizer_snapshot (GtkWidget *widget, - GtkSnapshot *snapshot) -{ - static const gdouble dashes[] = { 1.0, 2.0 }; - SysprofDuplexVisualizer *self = (SysprofDuplexVisualizer *)widget; - PangoFontDescription *font_desc; - GtkStyleContext *style_context; - PangoLayout *layout; - cairo_t *cr; - GtkAllocation alloc; - GdkRGBA fg; - guint mid; - - g_assert (SYSPROF_IS_DUPLEX_VISUALIZER (self)); - g_assert (snapshot != NULL); - - /* FIXME: This should all be drawn offscreen and then drawn to the snapshot - * using GdkMemoryTexture so that we can avoid extra GPU uploads. - */ - - gtk_widget_get_allocation (widget, &alloc); - - mid = alloc.height / 2; - - GTK_WIDGET_CLASS (sysprof_duplex_visualizer_parent_class)->snapshot (widget, snapshot); - - cr = gtk_snapshot_append_cairo (snapshot, &GRAPHENE_RECT_INIT (0, 0, alloc.width, alloc.height)); - - style_context = gtk_widget_get_style_context (widget); - gtk_style_context_get_color (style_context, &fg); - fg.alpha *= 0.4; - - /* Draw our center line */ - cairo_save (cr); - cairo_set_line_width (cr, 1.0); - cairo_set_dash (cr, dashes, G_N_ELEMENTS (dashes), 0); - cairo_move_to (cr, 0, mid); - cairo_line_to (cr, alloc.width, mid); - gdk_cairo_set_source_rgba (cr, &fg); - cairo_stroke (cr); - cairo_restore (cr); - - if (self->cache != NULL) - { - g_autofree SysprofVisualizerAbsolutePoint *points = NULL; - const Point *fpoints; - guint n_fpoints = 0; - - cairo_save (cr); - cairo_set_line_width (cr, 1.0); - if (self->rx_rgba_set) - gdk_cairo_set_source_rgba (cr, &self->rx_rgba); - - fpoints = point_cache_get_points (self->cache, self->rx_counter, &n_fpoints); - - if (n_fpoints > 0) - { - GdkRGBA rgba = self->rx_rgba; - gdouble last_x; - gdouble last_y; - guint p; - - points = g_realloc_n (points, n_fpoints, sizeof *points); - - sysprof_visualizer_translate_points (SYSPROF_VISUALIZER (self), - (const SysprofVisualizerRelativePoint *)fpoints, - n_fpoints, - points, - n_fpoints); - - /* Skip past data that we won't see anyway */ -#if 0 - for (p = 0; p < n_fpoints; p++) - { - if (points[p].x >= clip.x) - break; - } - - if (p >= n_fpoints) - return; -#endif - - /* But get at least one data point to anchor out of view */ - if (p > 0) - p--; - - last_x = points[p].x; - last_y = points[p].y; - - cairo_move_to (cr, last_x, mid); - cairo_line_to (cr, last_x, last_y); - - for (guint i = p + 1; i < n_fpoints; i++) - { - cairo_curve_to (cr, - last_x + ((points[i].x - last_x) / 2), - last_y, - last_x + ((points[i].x - last_x) / 2), - points[i].y, - points[i].x, - points[i].y); - - last_x = points[i].x; - last_y = points[i].y; - -#if 0 - if (points[i].x > clip.x + clip.width) - break; -#endif - } - - cairo_line_to (cr, last_x, mid); - cairo_close_path (cr); - cairo_stroke_preserve (cr); - rgba.alpha *= 0.5; - gdk_cairo_set_source_rgba (cr, &rgba); - cairo_fill (cr); - } - - cairo_restore (cr); - - /* AND NOW Tx */ - - cairo_save (cr); - cairo_set_line_width (cr, 1.0); - if (self->tx_rgba_set) - gdk_cairo_set_source_rgba (cr, &self->tx_rgba); - - fpoints = point_cache_get_points (self->cache, self->tx_counter, &n_fpoints); - - if (n_fpoints > 0) - { - GdkRGBA rgba = self->tx_rgba; - gdouble last_x; - gdouble last_y; - guint p; - - points = g_realloc_n (points, n_fpoints, sizeof *points); - - sysprof_visualizer_translate_points (SYSPROF_VISUALIZER (self), - (const SysprofVisualizerRelativePoint *)fpoints, - n_fpoints, - points, - n_fpoints); - -#if 0 - /* Skip past data that we won't see anyway */ - for (p = 0; p < n_fpoints; p++) - { - if (points[p].x >= clip.x) - break; - } - - if (p >= n_fpoints) - return ret; -#endif - - /* But get at least one data point to anchor out of view */ - if (p > 0) - p--; - - last_x = points[p].x; - last_y = points[p].y; - - cairo_move_to (cr, last_x, mid); - cairo_line_to (cr, last_x, last_y); - - for (guint i = p + 1; i < n_fpoints; i++) - { - cairo_curve_to (cr, - last_x + ((points[i].x - last_x) / 2), - last_y, - last_x + ((points[i].x - last_x) / 2), - points[i].y, - points[i].x, - points[i].y); - - last_x = points[i].x; - last_y = points[i].y; - -#if 0 - if (points[i].x > clip.x + clip.width) - break; -#endif - } - - cairo_line_to (cr, last_x, mid); - cairo_close_path (cr); - cairo_stroke_preserve (cr); - rgba.alpha *= 0.5; - gdk_cairo_set_source_rgba (cr, &rgba); - cairo_fill (cr); - } - - cairo_restore (cr); - } - - layout = gtk_widget_create_pango_layout (widget, ""); - - font_desc = pango_font_description_new (); - pango_font_description_set_family_static (font_desc, "Monospace"); - pango_font_description_set_absolute_size (font_desc, LABEL_HEIGHT_PX * PANGO_SCALE); - pango_layout_set_font_description (layout, font_desc); - - gdk_cairo_set_source_rgba (cr, &fg); - - cairo_move_to (cr, 2, 2); - if (self->rx_label != NULL) - pango_layout_set_text (layout, self->rx_label, -1); - else - pango_layout_set_text (layout, "RX", 2); - pango_cairo_show_layout (cr, layout); - - cairo_move_to (cr, 2, mid + 2); - if (self->tx_label != NULL) - pango_layout_set_text (layout, self->tx_label, -1); - else - pango_layout_set_text (layout, "TX", 2); - pango_cairo_show_layout (cr, layout); - - pango_font_description_free (font_desc); - g_object_unref (layout); - - cairo_destroy (cr); -} - -static void -sysprof_duplex_visualizer_finalize (GObject *object) -{ - SysprofDuplexVisualizer *self = (SysprofDuplexVisualizer *)object; - - g_clear_pointer (&self->cache, point_cache_unref); - g_clear_pointer (&self->rx_label, g_free); - g_clear_pointer (&self->tx_label, g_free); - - G_OBJECT_CLASS (sysprof_duplex_visualizer_parent_class)->finalize (object); -} - -static void -sysprof_duplex_visualizer_class_init (SysprofDuplexVisualizerClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); - SysprofVisualizerClass *visualizer_class = SYSPROF_VISUALIZER_CLASS (klass); - - object_class->finalize = sysprof_duplex_visualizer_finalize; - - widget_class->snapshot = sysprof_duplex_visualizer_snapshot; - - visualizer_class->set_reader = sysprof_duplex_visualizer_set_reader; -} - -static void -sysprof_duplex_visualizer_init (SysprofDuplexVisualizer *self) -{ - self->use_diff = TRUE; -} - -GtkWidget * -sysprof_duplex_visualizer_new (void) -{ - return g_object_new (SYSPROF_TYPE_DUPLEX_VISUALIZER, NULL); -} - -void -sysprof_duplex_visualizer_set_counters (SysprofDuplexVisualizer *self, - guint rx_counter, - guint tx_counter) -{ - g_return_if_fail (SYSPROF_IS_DUPLEX_VISUALIZER (self)); - g_return_if_fail (rx_counter != 0); - g_return_if_fail (tx_counter != 0); - - self->rx_counter = rx_counter; - self->tx_counter = tx_counter; -} - -void -sysprof_duplex_visualizer_set_colors (SysprofDuplexVisualizer *self, - const GdkRGBA *rx_rgba, - const GdkRGBA *tx_rgba) -{ - g_return_if_fail (SYSPROF_IS_DUPLEX_VISUALIZER (self)); - - if (rx_rgba) - self->rx_rgba = *rx_rgba; - self->rx_rgba_set = !!rx_rgba; - - if (tx_rgba) - self->tx_rgba = *tx_rgba; - self->tx_rgba_set = !!tx_rgba; - - gtk_widget_queue_draw (GTK_WIDGET (self)); -} - -gboolean -sysprof_duplex_visualizer_get_use_diff (SysprofDuplexVisualizer *self) -{ - g_return_val_if_fail (SYSPROF_IS_DUPLEX_VISUALIZER (self), FALSE); - - return self->use_diff; -} - -void -sysprof_duplex_visualizer_set_use_diff (SysprofDuplexVisualizer *self, - gboolean use_diff) -{ - g_return_if_fail (SYSPROF_IS_DUPLEX_VISUALIZER (self)); - - self->use_diff = !!use_diff; - gtk_widget_queue_allocate (GTK_WIDGET (self)); -} - -void -sysprof_duplex_visualizer_set_labels (SysprofDuplexVisualizer *self, - const gchar *rx_label, - const gchar *tx_label) -{ - g_return_if_fail (SYSPROF_IS_DUPLEX_VISUALIZER (self)); - - if (g_strcmp0 (rx_label, self->rx_label) != 0) - { - g_free (self->rx_label); - self->rx_label = g_strdup (rx_label); - } - - if (g_strcmp0 (tx_label, self->tx_label) != 0) - { - g_free (self->tx_label); - self->tx_label = g_strdup (tx_label); - } - - gtk_widget_queue_draw (GTK_WIDGET (self)); -} diff --git a/src/libsysprof-ui/sysprof-duplex-visualizer.h b/src/libsysprof-ui/sysprof-duplex-visualizer.h deleted file mode 100644 index 0b7dec53..00000000 --- a/src/libsysprof-ui/sysprof-duplex-visualizer.h +++ /dev/null @@ -1,45 +0,0 @@ -/* sysprof-duplex-visualizer.h - * - * Copyright 2019 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-visualizer.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_DUPLEX_VISUALIZER (sysprof_duplex_visualizer_get_type()) - -G_DECLARE_FINAL_TYPE (SysprofDuplexVisualizer, sysprof_duplex_visualizer, SYSPROF, DUPLEX_VISUALIZER, SysprofVisualizer) - -GtkWidget *sysprof_duplex_visualizer_new (void); -gboolean sysprof_duplex_visualizer_get_use_diff (SysprofDuplexVisualizer *self); -void sysprof_duplex_visualizer_set_use_diff (SysprofDuplexVisualizer *self, - gboolean use_diff); -void sysprof_duplex_visualizer_set_labels (SysprofDuplexVisualizer *self, - const gchar *rx_label, - const gchar *tx_label); -void sysprof_duplex_visualizer_set_counters (SysprofDuplexVisualizer *self, - guint rx_counter, - guint tx_counter); -void sysprof_duplex_visualizer_set_colors (SysprofDuplexVisualizer *self, - const GdkRGBA *rx_rgba, - const GdkRGBA *tx_rgba); - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-environ-editor-row.c b/src/libsysprof-ui/sysprof-environ-editor-row.c deleted file mode 100644 index a7dd7b0c..00000000 --- a/src/libsysprof-ui/sysprof-environ-editor-row.c +++ /dev/null @@ -1,277 +0,0 @@ -/* sysprof-environ-editor-row.c - * - * Copyright 2016-2019 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 - */ - -#define G_LOG_DOMAIN "sysprof-environ-editor-row" - -#include "config.h" - -#include "sysprof-environ-editor-row.h" - -struct _SysprofEnvironEditorRow -{ - GtkListBoxRow parent_instance; - - SysprofEnvironVariable *variable; - - GtkEntry *key_entry; - GtkEntry *value_entry; - GtkButton *delete_button; - - GBinding *key_binding; - GBinding *value_binding; -}; - -enum { - PROP_0, - PROP_VARIABLE, - LAST_PROP -}; - -enum { - DELETE, - LAST_SIGNAL -}; - -G_DEFINE_TYPE (SysprofEnvironEditorRow, sysprof_environ_editor_row, GTK_TYPE_LIST_BOX_ROW) - -static GParamSpec *properties [LAST_PROP]; -static guint signals [LAST_SIGNAL]; - -static gboolean -null_safe_mapping (GBinding *binding, - const GValue *from_value, - GValue *to_value, - gpointer user_data) -{ - const gchar *str = g_value_get_string (from_value); - g_value_set_string (to_value, str ?: ""); - return TRUE; -} - -static void -sysprof_environ_editor_row_connect (SysprofEnvironEditorRow *self) -{ - g_assert (SYSPROF_IS_ENVIRON_EDITOR_ROW (self)); - g_assert (SYSPROF_IS_ENVIRON_VARIABLE (self->variable)); - - self->key_binding = - g_object_bind_property_full (self->variable, "key", self->key_entry, "text", - G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL, - null_safe_mapping, NULL, NULL, NULL); - - self->value_binding = - g_object_bind_property_full (self->variable, "value", self->value_entry, "text", - G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL, - null_safe_mapping, NULL, NULL, NULL); -} - -static void -sysprof_environ_editor_row_disconnect (SysprofEnvironEditorRow *self) -{ - g_assert (SYSPROF_IS_ENVIRON_EDITOR_ROW (self)); - g_assert (SYSPROF_IS_ENVIRON_VARIABLE (self->variable)); - - g_clear_pointer (&self->key_binding, g_binding_unbind); - g_clear_pointer (&self->value_binding, g_binding_unbind); -} - -static void -delete_button_clicked (GtkButton *button, - SysprofEnvironEditorRow *self) -{ - g_assert (GTK_IS_BUTTON (button)); - g_assert (SYSPROF_IS_ENVIRON_EDITOR_ROW (self)); - - g_signal_emit (self, signals [DELETE], 0); -} - -static void -key_entry_activate (GtkWidget *entry, - SysprofEnvironEditorRow *self) -{ - g_assert (GTK_IS_ENTRY (entry)); - g_assert (SYSPROF_IS_ENVIRON_EDITOR_ROW (self)); - - gtk_widget_grab_focus (GTK_WIDGET (self->value_entry)); -} - -static void -value_entry_activate (GtkWidget *entry, - SysprofEnvironEditorRow *self) -{ - GtkWidget *parent; - - g_assert (GTK_IS_ENTRY (entry)); - g_assert (SYSPROF_IS_ENVIRON_EDITOR_ROW (self)); - - gtk_widget_grab_focus (GTK_WIDGET (self)); - parent = gtk_widget_get_ancestor (GTK_WIDGET (self), GTK_TYPE_LIST_BOX); - g_signal_emit_by_name (parent, "move-cursor", GTK_MOVEMENT_DISPLAY_LINES, 1); -} - -static void -sysprof_environ_editor_row_dispose (GObject *object) -{ - SysprofEnvironEditorRow *self = (SysprofEnvironEditorRow *)object; - - if (self->variable != NULL) - { - sysprof_environ_editor_row_disconnect (self); - g_clear_object (&self->variable); - } - - G_OBJECT_CLASS (sysprof_environ_editor_row_parent_class)->dispose (object); -} - -static void -sysprof_environ_editor_row_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - SysprofEnvironEditorRow *self = SYSPROF_ENVIRON_EDITOR_ROW (object); - - switch (prop_id) - { - case PROP_VARIABLE: - g_value_set_object (value, sysprof_environ_editor_row_get_variable (self)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_environ_editor_row_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - SysprofEnvironEditorRow *self = SYSPROF_ENVIRON_EDITOR_ROW (object); - - switch (prop_id) - { - case PROP_VARIABLE: - sysprof_environ_editor_row_set_variable (self, g_value_get_object (value)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_environ_editor_row_class_init (SysprofEnvironEditorRowClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); - - object_class->dispose = sysprof_environ_editor_row_dispose; - object_class->get_property = sysprof_environ_editor_row_get_property; - object_class->set_property = sysprof_environ_editor_row_set_property; - - gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/sysprof/ui/sysprof-environ-editor-row.ui"); - gtk_widget_class_bind_template_child (widget_class, SysprofEnvironEditorRow, delete_button); - gtk_widget_class_bind_template_child (widget_class, SysprofEnvironEditorRow, key_entry); - gtk_widget_class_bind_template_child (widget_class, SysprofEnvironEditorRow, value_entry); - - properties [PROP_VARIABLE] = - g_param_spec_object ("variable", - "Variable", - "Variable", - SYSPROF_TYPE_ENVIRON_VARIABLE, - (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_properties (object_class, LAST_PROP, properties); - - signals [DELETE] = - g_signal_new ("delete", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, - 0, NULL, NULL, NULL, G_TYPE_NONE, 0); -} - -static void -sysprof_environ_editor_row_init (SysprofEnvironEditorRow *self) -{ - gtk_widget_init_template (GTK_WIDGET (self)); - - g_signal_connect (self->delete_button, - "clicked", - G_CALLBACK (delete_button_clicked), - self); - - g_signal_connect (self->key_entry, - "activate", - G_CALLBACK (key_entry_activate), - self); - - g_signal_connect (self->value_entry, - "activate", - G_CALLBACK (value_entry_activate), - self); -} - -/** - * sysprof_environ_editor_row_get_variable: - * - * Returns: (transfer none) (nullable): An #SysprofEnvironVariable. - */ -SysprofEnvironVariable * -sysprof_environ_editor_row_get_variable (SysprofEnvironEditorRow *self) -{ - g_return_val_if_fail (SYSPROF_IS_ENVIRON_EDITOR_ROW (self), NULL); - - return self->variable; -} - -void -sysprof_environ_editor_row_set_variable (SysprofEnvironEditorRow *self, - SysprofEnvironVariable *variable) -{ - g_return_if_fail (SYSPROF_IS_ENVIRON_EDITOR_ROW (self)); - g_return_if_fail (!variable || SYSPROF_IS_ENVIRON_VARIABLE (variable)); - - if (variable != self->variable) - { - if (self->variable != NULL) - { - sysprof_environ_editor_row_disconnect (self); - g_clear_object (&self->variable); - } - - if (variable != NULL) - { - self->variable = g_object_ref (variable); - sysprof_environ_editor_row_connect (self); - } - - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_VARIABLE]); - } -} - -void -sysprof_environ_editor_row_start_editing (SysprofEnvironEditorRow *self) -{ - g_return_if_fail (SYSPROF_IS_ENVIRON_EDITOR_ROW (self)); - - gtk_widget_grab_focus (GTK_WIDGET (self->key_entry)); -} diff --git a/src/libsysprof-ui/sysprof-environ-editor-row.h b/src/libsysprof-ui/sysprof-environ-editor-row.h deleted file mode 100644 index 39437e5a..00000000 --- a/src/libsysprof-ui/sysprof-environ-editor-row.h +++ /dev/null @@ -1,38 +0,0 @@ -/* ide-environ-editor-row.h - * - * Copyright 2016-2019 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-environ-variable.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_ENVIRON_EDITOR_ROW (sysprof_environ_editor_row_get_type()) - -G_DECLARE_FINAL_TYPE (SysprofEnvironEditorRow, sysprof_environ_editor_row, SYSPROF, ENVIRON_EDITOR_ROW, GtkListBoxRow) - -SysprofEnvironVariable *sysprof_environ_editor_row_get_variable (SysprofEnvironEditorRow *self); -void sysprof_environ_editor_row_set_variable (SysprofEnvironEditorRow *self, - SysprofEnvironVariable *variable); -void sysprof_environ_editor_row_start_editing (SysprofEnvironEditorRow *self); - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-environ-editor-row.ui b/src/libsysprof-ui/sysprof-environ-editor-row.ui deleted file mode 100644 index 8735f770..00000000 --- a/src/libsysprof-ui/sysprof-environ-editor-row.ui +++ /dev/null @@ -1,40 +0,0 @@ - - - - diff --git a/src/libsysprof-ui/sysprof-environ-editor.c b/src/libsysprof-ui/sysprof-environ-editor.c deleted file mode 100644 index 7c641aca..00000000 --- a/src/libsysprof-ui/sysprof-environ-editor.c +++ /dev/null @@ -1,343 +0,0 @@ -/* sysprof-environ-editor.c - * - * Copyright 2016-2019 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 - */ - -#define G_LOG_DOMAIN "sysprof-environ-editor" - -#include "config.h" - -#include - -#include "sysprof-environ-editor.h" -#include "sysprof-environ-editor-row.h" -#include "sysprof-theme-manager.h" - -struct _SysprofEnvironEditor -{ - GtkWidget parent_instance; - GtkListBox *list_box; - SysprofEnviron *environ; - GtkWidget *dummy_row; - SysprofEnvironVariable *dummy; -}; - -G_DEFINE_TYPE (SysprofEnvironEditor, sysprof_environ_editor, GTK_TYPE_WIDGET) - -enum { - PROP_0, - PROP_ENVIRON, - N_PROPS -}; - -static GParamSpec *properties [N_PROPS]; - -static void -sysprof_environ_editor_delete_row (SysprofEnvironEditor *self, - SysprofEnvironEditorRow *row) -{ - SysprofEnvironVariable *variable; - - g_assert (SYSPROF_IS_ENVIRON_EDITOR (self)); - g_assert (SYSPROF_IS_ENVIRON_EDITOR_ROW (row)); - - variable = sysprof_environ_editor_row_get_variable (row); - sysprof_environ_remove (self->environ, variable); -} - -static GtkWidget * -sysprof_environ_editor_create_dummy_row (SysprofEnvironEditor *self) -{ - GtkWidget *row; - GtkWidget *label; - - g_assert (SYSPROF_IS_ENVIRON_EDITOR (self)); - - label = g_object_new (GTK_TYPE_LABEL, - "label", _("New environment variable…"), - "margin-start", 6, - "margin-end", 6, - "margin-top", 6, - "margin-bottom", 6, - "visible", TRUE, - "xalign", 0.0f, - NULL); - gtk_style_context_add_class (gtk_widget_get_style_context (label), "dim-label"); - - row = g_object_new (GTK_TYPE_LIST_BOX_ROW, - "child", label, - "visible", TRUE, - NULL); - - return row; -} - -static GtkWidget * -sysprof_environ_editor_create_row (gpointer item, - gpointer user_data) -{ - SysprofEnvironVariable *variable = item; - SysprofEnvironEditor *self = user_data; - SysprofEnvironEditorRow *row; - - g_assert (SYSPROF_IS_ENVIRON_EDITOR (self)); - g_assert (SYSPROF_IS_ENVIRON_VARIABLE (variable)); - - row = g_object_new (SYSPROF_TYPE_ENVIRON_EDITOR_ROW, - "variable", variable, - "visible", TRUE, - NULL); - - g_signal_connect_object (row, - "delete", - G_CALLBACK (sysprof_environ_editor_delete_row), - self, - G_CONNECT_SWAPPED); - - return GTK_WIDGET (row); -} - -static void -sysprof_environ_editor_disconnect (SysprofEnvironEditor *self) -{ - g_assert (SYSPROF_IS_ENVIRON_EDITOR (self)); - g_assert (SYSPROF_IS_ENVIRON (self->environ)); - - gtk_list_box_bind_model (self->list_box, NULL, NULL, NULL, NULL); - g_clear_object (&self->dummy); -} - -static void -sysprof_environ_editor_connect (SysprofEnvironEditor *self) -{ - g_assert (SYSPROF_IS_ENVIRON_EDITOR (self)); - g_assert (SYSPROF_IS_ENVIRON (self->environ)); - - gtk_list_box_bind_model (self->list_box, - G_LIST_MODEL (self->environ), - sysprof_environ_editor_create_row, self, NULL); - - self->dummy_row = sysprof_environ_editor_create_dummy_row (self); - gtk_list_box_append (self->list_box, self->dummy_row); -} - -static void -find_row_cb (GtkWidget *widget, - gpointer data) -{ - struct { - SysprofEnvironVariable *variable; - SysprofEnvironEditorRow *row; - } *lookup = data; - - g_assert (lookup != NULL); - g_assert (GTK_IS_LIST_BOX_ROW (widget)); - - if (SYSPROF_IS_ENVIRON_EDITOR_ROW (widget)) - { - SysprofEnvironVariable *variable; - - variable = sysprof_environ_editor_row_get_variable (SYSPROF_ENVIRON_EDITOR_ROW (widget)); - - if (variable == lookup->variable) - lookup->row = SYSPROF_ENVIRON_EDITOR_ROW (widget); - } -} - -static SysprofEnvironEditorRow * -find_row (SysprofEnvironEditor *self, - SysprofEnvironVariable *variable) -{ - struct { - SysprofEnvironVariable *variable; - SysprofEnvironEditorRow *row; - } lookup = { variable, NULL }; - - g_assert (SYSPROF_IS_ENVIRON_EDITOR (self)); - g_assert (SYSPROF_IS_ENVIRON_VARIABLE (variable)); - - for (GtkWidget *child = gtk_widget_get_first_child (GTK_WIDGET (self->list_box)); - child; - child = gtk_widget_get_next_sibling (child)) - find_row_cb (child, &lookup); - - return lookup.row; -} - -static void -sysprof_environ_editor_row_activated (SysprofEnvironEditor *self, - GtkListBoxRow *row, - GtkListBox *list_box) -{ - g_assert (GTK_IS_LIST_BOX (list_box)); - g_assert (GTK_IS_LIST_BOX_ROW (row)); - - if (self->environ == NULL) - return; - - if (self->dummy_row == GTK_WIDGET (row)) - { - g_autoptr(SysprofEnvironVariable) variable = NULL; - - variable = sysprof_environ_variable_new (NULL, NULL); - sysprof_environ_append (self->environ, variable); - sysprof_environ_editor_row_start_editing (find_row (self, variable)); - } -} - -static void -sysprof_environ_editor_dispose (GObject *object) -{ - SysprofEnvironEditor *self = (SysprofEnvironEditor *)object; - - if (self->list_box) - { - gtk_widget_unparent (GTK_WIDGET (self->list_box)); - self->list_box = NULL; - } - - g_clear_object (&self->environ); - - G_OBJECT_CLASS (sysprof_environ_editor_parent_class)->dispose (object); -} - -static void -sysprof_environ_editor_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - SysprofEnvironEditor *self = SYSPROF_ENVIRON_EDITOR (object); - - switch (prop_id) - { - case PROP_ENVIRON: - g_value_set_object (value, sysprof_environ_editor_get_environ (self)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); - } -} - -static void -sysprof_environ_editor_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - SysprofEnvironEditor *self = SYSPROF_ENVIRON_EDITOR (object); - - switch (prop_id) - { - case PROP_ENVIRON: - sysprof_environ_editor_set_environ (self, g_value_get_object (value)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); - } -} - -static void -sysprof_environ_editor_class_init (SysprofEnvironEditorClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); - SysprofThemeManager *theme_manager = sysprof_theme_manager_get_default (); - - object_class->dispose = sysprof_environ_editor_dispose; - object_class->get_property = sysprof_environ_editor_get_property; - object_class->set_property = sysprof_environ_editor_set_property; - - properties [PROP_ENVIRON] = - g_param_spec_object ("environ", - "Environment", - "Environment", - SYSPROF_TYPE_ENVIRON, - (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_properties (object_class, N_PROPS, properties); - - gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT); - - sysprof_theme_manager_register_resource (theme_manager, NULL, NULL, "/org/gnome/sysprof/css/SysprofEnvironEditor-shared.css"); -} - -static void -sysprof_environ_editor_init (SysprofEnvironEditor *self) -{ - self->list_box = GTK_LIST_BOX (gtk_list_box_new ()); - gtk_widget_set_parent (GTK_WIDGET (self->list_box), GTK_WIDGET (self)); - - gtk_list_box_set_selection_mode (self->list_box, GTK_SELECTION_NONE); - - gtk_widget_add_css_class (GTK_WIDGET (self), "environ-editor"); - - g_signal_connect_object (self->list_box, - "row-activated", - G_CALLBACK (sysprof_environ_editor_row_activated), - self, - G_CONNECT_SWAPPED); -} - -GtkWidget * -sysprof_environ_editor_new (void) -{ - return g_object_new (SYSPROF_TYPE_ENVIRON_EDITOR, NULL); -} - -void -sysprof_environ_editor_set_environ (SysprofEnvironEditor *self, - SysprofEnviron *environ_) -{ - g_return_if_fail (SYSPROF_IS_ENVIRON_EDITOR (self)); - g_return_if_fail (SYSPROF_IS_ENVIRON (environ_)); - - if (self->environ != environ_) - { - if (self->environ != NULL) - { - sysprof_environ_editor_disconnect (self); - g_clear_object (&self->environ); - } - - if (environ_ != NULL) - { - self->environ = g_object_ref (environ_); - sysprof_environ_editor_connect (self); - } - - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ENVIRON]); - } -} - -/** - * sysprof_environ_editor_get_environ: - * - * Returns: (nullable) (transfer none): An #SysprofEnviron or %NULL. - * - * Since: 3.34 - */ -SysprofEnviron * -sysprof_environ_editor_get_environ (SysprofEnvironEditor *self) -{ - g_return_val_if_fail (SYSPROF_IS_ENVIRON_EDITOR (self), NULL); - - return self->environ; -} diff --git a/src/libsysprof-ui/sysprof-environ-editor.h b/src/libsysprof-ui/sysprof-environ-editor.h deleted file mode 100644 index 403ab797..00000000 --- a/src/libsysprof-ui/sysprof-environ-editor.h +++ /dev/null @@ -1,38 +0,0 @@ -/* sysprof-environ-editor.h - * - * Copyright 2016-2019 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-environ.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_ENVIRON_EDITOR (sysprof_environ_editor_get_type()) - -G_DECLARE_FINAL_TYPE (SysprofEnvironEditor, sysprof_environ_editor, SYSPROF, ENVIRON_EDITOR, GtkWidget) - -GtkWidget *sysprof_environ_editor_new (void); -SysprofEnviron *sysprof_environ_editor_get_environ (SysprofEnvironEditor *self); -void sysprof_environ_editor_set_environ (SysprofEnvironEditor *self, - SysprofEnviron *environ); - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-environ-variable.c b/src/libsysprof-ui/sysprof-environ-variable.c deleted file mode 100644 index 660b3457..00000000 --- a/src/libsysprof-ui/sysprof-environ-variable.c +++ /dev/null @@ -1,185 +0,0 @@ -/* sysprof-environ-variable.c - * - * Copyright 2016-2019 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 - */ - -#define G_LOG_DOMAIN "sysprof-environ-variable" - -#include "config.h" - -#include "sysprof-environ-variable.h" - -struct _SysprofEnvironVariable -{ - GObject parent_instance; - gchar *key; - gchar *value; -}; - -G_DEFINE_TYPE (SysprofEnvironVariable, sysprof_environ_variable, G_TYPE_OBJECT) - -enum { - PROP_0, - PROP_KEY, - PROP_VALUE, - LAST_PROP -}; - -static GParamSpec *properties [LAST_PROP]; - -static void -sysprof_environ_variable_finalize (GObject *object) -{ - SysprofEnvironVariable *self = (SysprofEnvironVariable *)object; - - g_clear_pointer (&self->key, g_free); - g_clear_pointer (&self->value, g_free); - - G_OBJECT_CLASS (sysprof_environ_variable_parent_class)->finalize (object); -} - -static void -sysprof_environ_variable_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - SysprofEnvironVariable *self = SYSPROF_ENVIRON_VARIABLE(object); - - switch (prop_id) - { - case PROP_KEY: - g_value_set_string (value, self->key); - break; - - case PROP_VALUE: - g_value_set_string (value, self->value); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); - } -} - -static void -sysprof_environ_variable_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - SysprofEnvironVariable *self = SYSPROF_ENVIRON_VARIABLE(object); - - switch (prop_id) - { - case PROP_KEY: - sysprof_environ_variable_set_key (self, g_value_get_string (value)); - break; - - case PROP_VALUE: - sysprof_environ_variable_set_value (self, g_value_get_string (value)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); - } -} - -static void -sysprof_environ_variable_class_init (SysprofEnvironVariableClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->finalize = sysprof_environ_variable_finalize; - object_class->get_property = sysprof_environ_variable_get_property; - object_class->set_property = sysprof_environ_variable_set_property; - - properties [PROP_KEY] = - g_param_spec_string ("key", - "Key", - "The key for the environment variable", - NULL, - (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - properties [PROP_VALUE] = - g_param_spec_string ("value", - "Value", - "The value for the environment variable", - NULL, - (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_properties (object_class, LAST_PROP, properties); -} - -static void -sysprof_environ_variable_init (SysprofEnvironVariable *self) -{ -} - -const gchar * -sysprof_environ_variable_get_key (SysprofEnvironVariable *self) -{ - g_return_val_if_fail (SYSPROF_IS_ENVIRON_VARIABLE (self), NULL); - - return self->key; -} - -void -sysprof_environ_variable_set_key (SysprofEnvironVariable *self, - const gchar *key) -{ - g_return_if_fail (SYSPROF_IS_ENVIRON_VARIABLE (self)); - - if (g_strcmp0 (key, self->key) != 0) - { - g_free (self->key); - self->key = g_strdup (key); - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_KEY]); - } -} - -const gchar * -sysprof_environ_variable_get_value (SysprofEnvironVariable *self) -{ - g_return_val_if_fail (SYSPROF_IS_ENVIRON_VARIABLE (self), NULL); - - return self->value; -} - -void -sysprof_environ_variable_set_value (SysprofEnvironVariable *self, - const gchar *value) -{ - g_return_if_fail (SYSPROF_IS_ENVIRON_VARIABLE (self)); - - if (g_strcmp0 (value, self->value) != 0) - { - g_free (self->value); - self->value = g_strdup (value); - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_VALUE]); - } -} - -SysprofEnvironVariable * -sysprof_environ_variable_new (const gchar *key, - const gchar *value) -{ - return g_object_new (SYSPROF_TYPE_ENVIRON_VARIABLE, - "key", key, - "value", value, - NULL); -} diff --git a/src/libsysprof-ui/sysprof-environ-variable.h b/src/libsysprof-ui/sysprof-environ-variable.h deleted file mode 100644 index ee19876a..00000000 --- a/src/libsysprof-ui/sysprof-environ-variable.h +++ /dev/null @@ -1,40 +0,0 @@ -/* sysprof-environ-variable.h - * - * Copyright 2016-2019 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_ENVIRON_VARIABLE (sysprof_environ_variable_get_type()) - -G_DECLARE_FINAL_TYPE (SysprofEnvironVariable, sysprof_environ_variable, SYSPROF, ENVIRON_VARIABLE, GObject) - -SysprofEnvironVariable *sysprof_environ_variable_new (const gchar *key, - const gchar *value); -const gchar *sysprof_environ_variable_get_key (SysprofEnvironVariable *self); -void sysprof_environ_variable_set_key (SysprofEnvironVariable *self, - const gchar *key); -const gchar *sysprof_environ_variable_get_value (SysprofEnvironVariable *self); -void sysprof_environ_variable_set_value (SysprofEnvironVariable *self, - const gchar *value); - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-environ.c b/src/libsysprof-ui/sysprof-environ.c deleted file mode 100644 index a1200d46..00000000 --- a/src/libsysprof-ui/sysprof-environ.c +++ /dev/null @@ -1,379 +0,0 @@ -/* sysprof-environ.c - * - * Copyright 2016-2019 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 - */ - -#define G_LOG_DOMAIN "sysprof-environ" - -#include "config.h" - -#include - -#include "sysprof-environ.h" -#include "sysprof-environ-variable.h" - -struct _SysprofEnviron -{ - GObject parent_instance; - GPtrArray *variables; -}; - -static void list_model_iface_init (GListModelInterface *iface); - -G_DEFINE_TYPE_EXTENDED (SysprofEnviron, sysprof_environ, G_TYPE_OBJECT, 0, - G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init)) - -enum { - CHANGED, - LAST_SIGNAL -}; - -static guint signals [LAST_SIGNAL]; - -static void -sysprof_environ_finalize (GObject *object) -{ - SysprofEnviron *self = (SysprofEnviron *)object; - - g_clear_pointer (&self->variables, g_ptr_array_unref); - - G_OBJECT_CLASS (sysprof_environ_parent_class)->finalize (object); -} - -static void -sysprof_environ_class_init (SysprofEnvironClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->finalize = sysprof_environ_finalize; - - signals [CHANGED] = - g_signal_new ("changed", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, - 0, NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); - g_signal_set_va_marshaller (signals [CHANGED], - G_TYPE_FROM_CLASS (klass), - g_cclosure_marshal_VOID__VOIDv); -} - -static void -sysprof_environ_items_changed (SysprofEnviron *self) -{ - g_assert (SYSPROF_IS_ENVIRON (self)); - - g_signal_emit (self, signals [CHANGED], 0); -} - -static void -sysprof_environ_init (SysprofEnviron *self) -{ - self->variables = g_ptr_array_new_with_free_func (g_object_unref); - - g_signal_connect (self, - "items-changed", - G_CALLBACK (sysprof_environ_items_changed), - NULL); -} - -static GType -sysprof_environ_get_item_type (GListModel *model) -{ - return SYSPROF_TYPE_ENVIRON_VARIABLE; -} - -static gpointer -sysprof_environ_get_item (GListModel *model, - guint position) -{ - SysprofEnviron *self = (SysprofEnviron *)model; - - g_return_val_if_fail (SYSPROF_IS_ENVIRON (self), NULL); - g_return_val_if_fail (position < self->variables->len, NULL); - - return g_object_ref (g_ptr_array_index (self->variables, position)); -} - -static guint -sysprof_environ_get_n_items (GListModel *model) -{ - SysprofEnviron *self = (SysprofEnviron *)model; - - g_return_val_if_fail (SYSPROF_IS_ENVIRON (self), 0); - - return self->variables->len; -} - -static void -list_model_iface_init (GListModelInterface *iface) -{ - iface->get_n_items = sysprof_environ_get_n_items; - iface->get_item = sysprof_environ_get_item; - iface->get_item_type = sysprof_environ_get_item_type; -} - -static void -sysprof_environ_variable_notify (SysprofEnviron *self, - GParamSpec *pspec, - SysprofEnvironVariable *variable) -{ - g_assert (SYSPROF_IS_ENVIRON (self)); - - g_signal_emit (self, signals [CHANGED], 0); -} - -void -sysprof_environ_setenv (SysprofEnviron *self, - const gchar *key, - const gchar *value) -{ - guint i; - - g_return_if_fail (SYSPROF_IS_ENVIRON (self)); - g_return_if_fail (key != NULL); - - for (i = 0; i < self->variables->len; i++) - { - SysprofEnvironVariable *var = g_ptr_array_index (self->variables, i); - const gchar *var_key = sysprof_environ_variable_get_key (var); - - if (g_strcmp0 (key, var_key) == 0) - { - if (value == NULL) - { - g_ptr_array_remove_index (self->variables, i); - g_list_model_items_changed (G_LIST_MODEL (self), i, 1, 0); - return; - } - - sysprof_environ_variable_set_value (var, value); - return; - } - } - - if (value != NULL) - { - SysprofEnvironVariable *var; - guint position = self->variables->len; - - var = g_object_new (SYSPROF_TYPE_ENVIRON_VARIABLE, - "key", key, - "value", value, - NULL); - g_signal_connect_object (var, - "notify", - G_CALLBACK (sysprof_environ_variable_notify), - self, - G_CONNECT_SWAPPED); - g_ptr_array_add (self->variables, var); - g_list_model_items_changed (G_LIST_MODEL (self), position, 0, 1); - } -} - -const gchar * -sysprof_environ_getenv (SysprofEnviron *self, - const gchar *key) -{ - guint i; - - g_return_val_if_fail (SYSPROF_IS_ENVIRON (self), NULL); - g_return_val_if_fail (key != NULL, NULL); - - for (i = 0; i < self->variables->len; i++) - { - SysprofEnvironVariable *var = g_ptr_array_index (self->variables, i); - const gchar *var_key = sysprof_environ_variable_get_key (var); - - if (g_strcmp0 (key, var_key) == 0) - return sysprof_environ_variable_get_value (var); - } - - return NULL; -} - -/** - * sysprof_environ_get_environ: - * @self: An #SysprofEnviron - * - * Gets the environment as a set of key=value pairs, suitable for use - * in various GLib process functions. - * - * Returns: (transfer full): A newly allocated string array. - * - * Since: 3.32 - */ -gchar ** -sysprof_environ_get_environ (SysprofEnviron *self) -{ - GPtrArray *ar; - guint i; - - g_return_val_if_fail (SYSPROF_IS_ENVIRON (self), NULL); - - ar = g_ptr_array_new (); - - for (i = 0; i < self->variables->len; i++) - { - SysprofEnvironVariable *var = g_ptr_array_index (self->variables, i); - const gchar *key = sysprof_environ_variable_get_key (var); - const gchar *value = sysprof_environ_variable_get_value (var); - - if (value == NULL) - value = ""; - - if (key != NULL) - g_ptr_array_add (ar, g_strdup_printf ("%s=%s", key, value)); - } - - g_ptr_array_add (ar, NULL); - - return (gchar **)g_ptr_array_free (ar, FALSE); -} - -SysprofEnviron * -sysprof_environ_new (void) -{ - return g_object_new (SYSPROF_TYPE_ENVIRON, NULL); -} - -void -sysprof_environ_remove (SysprofEnviron *self, - SysprofEnvironVariable *variable) -{ - guint i; - - g_return_if_fail (SYSPROF_IS_ENVIRON (self)); - g_return_if_fail (SYSPROF_IS_ENVIRON_VARIABLE (variable)); - - for (i = 0; i < self->variables->len; i++) - { - SysprofEnvironVariable *item = g_ptr_array_index (self->variables, i); - - if (item == variable) - { - g_ptr_array_remove_index (self->variables, i); - g_list_model_items_changed (G_LIST_MODEL (self), i, 1, 0); - break; - } - } -} - -void -sysprof_environ_append (SysprofEnviron *self, - SysprofEnvironVariable *variable) -{ - guint position; - - g_return_if_fail (SYSPROF_IS_ENVIRON (self)); - g_return_if_fail (SYSPROF_IS_ENVIRON_VARIABLE (variable)); - - position = self->variables->len; - - g_signal_connect_object (variable, - "notify", - G_CALLBACK (sysprof_environ_variable_notify), - self, - G_CONNECT_SWAPPED); - g_ptr_array_add (self->variables, g_object_ref (variable)); - g_list_model_items_changed (G_LIST_MODEL (self), position, 0, 1); -} - -/** - * sysprof_environ_copy: - * @self: An #SysprofEnviron - * - * Copies the contents of #SysprofEnviron into a newly allocated #SysprofEnviron. - * - * Returns: (transfer full): An #SysprofEnviron. - * - * Since: 3.32 - */ -SysprofEnviron * -sysprof_environ_copy (SysprofEnviron *self) -{ - g_autoptr(SysprofEnviron) copy = NULL; - - g_return_val_if_fail (SYSPROF_IS_ENVIRON (self), NULL); - - copy = sysprof_environ_new (); - sysprof_environ_copy_into (self, copy, TRUE); - - return g_steal_pointer (©); -} - -void -sysprof_environ_copy_into (SysprofEnviron *self, - SysprofEnviron *dest, - gboolean replace) -{ - g_return_if_fail (SYSPROF_IS_ENVIRON (self)); - g_return_if_fail (SYSPROF_IS_ENVIRON (dest)); - - for (guint i = 0; i < self->variables->len; i++) - { - SysprofEnvironVariable *var = g_ptr_array_index (self->variables, i); - const gchar *key = sysprof_environ_variable_get_key (var); - const gchar *value = sysprof_environ_variable_get_value (var); - - if (replace || sysprof_environ_getenv (dest, key) == NULL) - sysprof_environ_setenv (dest, key, value); - } -} - -/** - * ide_environ_parse: - * @pair: the KEY=VALUE pair - * @key: (out) (optional): a location for a @key - * @value: (out) (optional): a location for a @value - * - * Parses a KEY=VALUE style key-pair into @key and @value. - * - * Returns: %TRUE if @pair was successfully parsed - * - * Since: 3.32 - */ -gboolean -ide_environ_parse (const gchar *pair, - gchar **key, - gchar **value) -{ - const gchar *eq; - - g_return_val_if_fail (pair != NULL, FALSE); - - if (key != NULL) - *key = NULL; - - if (value != NULL) - *value = NULL; - - if ((eq = strchr (pair, '='))) - { - if (key != NULL) - *key = g_strndup (pair, eq - pair); - - if (value != NULL) - *value = g_strdup (eq + 1); - - return TRUE; - } - - return FALSE; -} diff --git a/src/libsysprof-ui/sysprof-environ.h b/src/libsysprof-ui/sysprof-environ.h deleted file mode 100644 index ef5e5f83..00000000 --- a/src/libsysprof-ui/sysprof-environ.h +++ /dev/null @@ -1,52 +0,0 @@ -/* sysprof-environ.h - * - * Copyright 2016-2019 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-environ-variable.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_ENVIRON (sysprof_environ_get_type()) - -G_DECLARE_FINAL_TYPE (SysprofEnviron, sysprof_environ, SYSPROF, ENVIRON, GObject) - -gboolean ide_environ_parse (const gchar *pair, - gchar **key, - gchar **value); -SysprofEnviron *sysprof_environ_new (void); -void sysprof_environ_setenv (SysprofEnviron *self, - const gchar *key, - const gchar *value); -const gchar *sysprof_environ_getenv (SysprofEnviron *self, - const gchar *key); -gchar **sysprof_environ_get_environ (SysprofEnviron *self); -void sysprof_environ_append (SysprofEnviron *self, - SysprofEnvironVariable *variable); -void sysprof_environ_remove (SysprofEnviron *self, - SysprofEnvironVariable *variable); -SysprofEnviron *sysprof_environ_copy (SysprofEnviron *self); -void sysprof_environ_copy_into (SysprofEnviron *self, - SysprofEnviron *dest, - gboolean replace); - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-failed-state-view.c b/src/libsysprof-ui/sysprof-failed-state-view.c deleted file mode 100644 index 6ca02e7d..00000000 --- a/src/libsysprof-ui/sysprof-failed-state-view.c +++ /dev/null @@ -1,62 +0,0 @@ -/* sysprof-failed-state-view.c - * - * Copyright 2016-2019 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-failed-state-view.h" - -G_DEFINE_TYPE (SysprofFailedStateView, sysprof_failed_state_view, GTK_TYPE_WIDGET) - -GtkWidget * -sysprof_failed_state_view_new (void) -{ - return g_object_new (SYSPROF_TYPE_FAILED_STATE_VIEW, NULL); -} - -static void -sysprof_failed_state_view_dispose (GObject *object) -{ - SysprofFailedStateView *self = (SysprofFailedStateView *)object; - GtkWidget *child; - - while ((child = gtk_widget_get_first_child (GTK_WIDGET (self)))) - gtk_widget_unparent (child); - - G_OBJECT_CLASS (sysprof_failed_state_view_parent_class)->dispose (object); -} - -static void -sysprof_failed_state_view_class_init (SysprofFailedStateViewClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); - - object_class->dispose = sysprof_failed_state_view_dispose; - - gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT); - gtk_widget_class_set_template_from_resource (widget_class, - "/org/gnome/sysprof/ui/sysprof-failed-state-view.ui"); -} - -static void -sysprof_failed_state_view_init (SysprofFailedStateView *self) -{ - gtk_widget_init_template (GTK_WIDGET (self)); -} diff --git a/src/libsysprof-ui/sysprof-failed-state-view.h b/src/libsysprof-ui/sysprof-failed-state-view.h deleted file mode 100644 index 4657abe1..00000000 --- a/src/libsysprof-ui/sysprof-failed-state-view.h +++ /dev/null @@ -1,41 +0,0 @@ -/* sysprof-failed-state-view.h - * - * Copyright 2016-2019 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 - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_FAILED_STATE_VIEW (sysprof_failed_state_view_get_type()) - -G_DECLARE_DERIVABLE_TYPE (SysprofFailedStateView, sysprof_failed_state_view, SYSPROF, FAILED_STATE_VIEW, GtkWidget) - -struct _SysprofFailedStateViewClass -{ - GtkWidgetClass parent; -}; - -GtkWidget *sysprof_failed_state_view_new (void); -void sysprof_failed_state_view_set_profiler (SysprofFailedStateView *self, - SysprofProfiler *profiler); - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-failed-state-view.ui b/src/libsysprof-ui/sysprof-failed-state-view.ui deleted file mode 100644 index 8b01a009..00000000 --- a/src/libsysprof-ui/sysprof-failed-state-view.ui +++ /dev/null @@ -1,47 +0,0 @@ - - - - diff --git a/src/libsysprof-ui/sysprof-line-visualizer.c b/src/libsysprof-ui/sysprof-line-visualizer.c deleted file mode 100644 index 046be4a8..00000000 --- a/src/libsysprof-ui/sysprof-line-visualizer.c +++ /dev/null @@ -1,909 +0,0 @@ -/* sysprof-line-visualizer.c - * - * Copyright 2016-2019 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 - */ - -#define G_LOG_DOMAIN "sysprof-line-visualizer" - -#include "config.h" - -#include -#include -#include - -#include "pointcache.h" -#include "sysprof-line-visualizer.h" - -typedef struct -{ - /* - * Our reader as assigned by the visualizer system. - */ - SysprofCaptureReader *reader; - - /* - * An array of LineInfo which contains information about the counters - * we need to render. - */ - GArray *lines; - - /* - * This is our set of cached points to render. Once it is assigned here, - * it is immutable (and therefore may be shared with worker processes - * that are rendering the points). - */ - PointCache *cache; - - /* The format for units (such as mHz, Watts, etc). */ - gchar *units; - - /* - * Range of the scale for lower and upper. - */ - gdouble y_lower; - gdouble y_upper; - - /* - * If we have a new counter discovered or the reader is set, we might - * want to delay loading until we return to the main loop. This can - * help us avoid doing duplicate work. - */ - guint queued_load; - - guint y_lower_set : 1; - guint y_upper_set : 1; -} SysprofLineVisualizerPrivate; - -typedef struct -{ - guint id; - guint type; - gdouble line_width; - GdkRGBA foreground; - GdkRGBA background; - guint use_default_style : 1; - guint fill : 1; - guint use_dash : 1; -} LineInfo; - -typedef struct -{ - SysprofCaptureCursor *cursor; - GArray *lines; - PointCache *cache; - gint64 begin_time; - gint64 end_time; - gdouble y_lower; - gdouble y_upper; - guint y_lower_set : 1; - guint y_upper_set : 1; -} LoadData; - -G_DEFINE_TYPE_WITH_PRIVATE (SysprofLineVisualizer, sysprof_line_visualizer, SYSPROF_TYPE_VISUALIZER) - -static void sysprof_line_visualizer_load_data_async (SysprofLineVisualizer *self, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -static PointCache *sysprof_line_visualizer_load_data_finish (SysprofLineVisualizer *self, - GAsyncResult *result, - GError **error); - -enum { - PROP_0, - PROP_Y_LOWER, - PROP_Y_UPPER, - PROP_UNITS, - N_PROPS -}; - -static GParamSpec *properties [N_PROPS]; -static const gdouble dashes[] = { 1.0, 2.0 }; - -static void -load_data_free (gpointer data) -{ - LoadData *load = data; - - if (load != NULL) - { - g_clear_pointer (&load->lines, g_array_unref); - g_clear_pointer (&load->cursor, sysprof_capture_cursor_unref); - g_clear_pointer (&load->cache, point_cache_unref); - g_slice_free (LoadData, load); - } -} - -static GArray * -copy_array (GArray *ar) -{ - GArray *ret; - - ret = g_array_sized_new (FALSE, FALSE, g_array_get_element_size (ar), ar->len); - g_array_set_size (ret, ar->len); - memcpy (ret->data, ar->data, ar->len * g_array_get_element_size (ret)); - - return ret; -} - -static void -sysprof_line_visualizer_snapshot (GtkWidget *widget, - GtkSnapshot *snapshot) -{ - static PangoAttrList *attrs = NULL; - SysprofLineVisualizer *self = (SysprofLineVisualizer *)widget; - SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self); - g_autofree gchar *upper = NULL; - GtkStyleContext *style_context; - PangoLayout *layout; - cairo_t *cr; - GtkAllocation alloc; - GdkRectangle clip; - GdkRGBA foreground; - - g_assert (SYSPROF_IS_LINE_VISUALIZER (widget)); - g_assert (snapshot != NULL); - - gtk_widget_get_allocation (widget, &alloc); - - GTK_WIDGET_CLASS (sysprof_line_visualizer_parent_class)->snapshot (widget, snapshot); - - if (priv->cache == NULL) - return; - -#if 0 - if (!gdk_cairo_get_clip_rectangle (cr, &clip)) - return ret; -#else - clip.x = 0; - clip.y = 0; - clip.width = alloc.width; - clip.height = alloc.height; -#endif - - cr = gtk_snapshot_append_cairo (snapshot, &GRAPHENE_RECT_INIT (0, 0, alloc.width, alloc.height)); - - style_context = gtk_widget_get_style_context (widget); - gtk_style_context_get_color (style_context, &foreground); - - for (guint line = 0; line < priv->lines->len; line++) - { - g_autofree SysprofVisualizerAbsolutePoint *points = NULL; - const LineInfo *line_info = &g_array_index (priv->lines, LineInfo, line); - const Point *fpoints; - guint n_fpoints = 0; - GdkRGBA color; - - fpoints = point_cache_get_points (priv->cache, line_info->id, &n_fpoints); - - if (n_fpoints > 0) - { - gdouble last_x = 0; - gdouble last_y = 0; - guint p; - - points = g_new0 (SysprofVisualizerAbsolutePoint, n_fpoints); - - sysprof_visualizer_translate_points (SYSPROF_VISUALIZER (self), - (const SysprofVisualizerRelativePoint *)fpoints, - n_fpoints, - points, - n_fpoints); - - for (p = 0; p < n_fpoints; p++) - { - if (points[p].x >= clip.x) - break; - } - - if (p >= n_fpoints) - goto cleanup; - - if (p > 0) - p--; - - last_x = points[p].x; - last_y = points[p].y; - - if (line_info->fill) - { - cairo_move_to (cr, last_x, alloc.height); - cairo_line_to (cr, last_x, last_y); - } - else - { - cairo_move_to (cr, last_x, last_y); - } - - for (guint i = p + 1; i < n_fpoints; i++) - { - cairo_curve_to (cr, - last_x + ((points[i].x - last_x) / 2), - last_y, - last_x + ((points[i].x - last_x) / 2), - points[i].y, - points[i].x, - points[i].y); - - last_x = points[i].x; - last_y = points[i].y; - - if (points[i].x > clip.x + clip.width) - break; - } - - if (line_info->fill) - { - cairo_line_to (cr, last_x, alloc.height); - cairo_close_path (cr); - } - - cairo_set_line_width (cr, line_info->line_width); - - if (line_info->use_dash) - cairo_set_dash (cr, dashes, G_N_ELEMENTS (dashes), 0); - - if (line_info->fill) - { - gdk_cairo_set_source_rgba (cr, &line_info->background); - cairo_fill_preserve (cr); - } - - if (line_info->use_default_style) - color = foreground; - else - color = line_info->foreground; - - gdk_cairo_set_source_rgba (cr, &color); - cairo_stroke (cr); - } - } - - if (!attrs) - { - attrs = pango_attr_list_new (); - pango_attr_list_insert (attrs, pango_attr_scale_new (0.666)); - } - - if (priv->y_upper != 100.0) - { - if (priv->units) - upper = g_strdup_printf ("%lg %s", priv->y_upper, priv->units); - else - upper = g_strdup_printf ("%lg", priv->y_upper); - - layout = gtk_widget_create_pango_layout (widget, upper); - pango_layout_set_attributes (layout, attrs); - cairo_move_to (cr, 2, 2); - foreground.alpha *= 0.5; - gdk_cairo_set_source_rgba (cr, &foreground); - pango_cairo_show_layout (cr, layout); - g_clear_object (&layout); - } - -cleanup: - cairo_destroy (cr); -} - -static void -sysprof_line_visualizer_load_data_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - SysprofLineVisualizer *self = (SysprofLineVisualizer *)object; - SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self); - g_autoptr(GError) error = NULL; - g_autoptr(PointCache) cache = NULL; - - g_assert (SYSPROF_IS_LINE_VISUALIZER (self)); - - cache = sysprof_line_visualizer_load_data_finish (self, result, &error); - - if (cache == NULL) - { - g_warning ("%s", error->message); - return; - } - - g_clear_pointer (&priv->cache, point_cache_unref); - priv->cache = g_steal_pointer (&cache); - - gtk_widget_queue_draw (GTK_WIDGET (self)); -} - -static gboolean -sysprof_line_visualizer_do_reload (gpointer data) -{ - SysprofLineVisualizer *self = data; - SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self); - - g_assert (SYSPROF_IS_LINE_VISUALIZER (self)); - - priv->queued_load = 0; - - if (priv->reader != NULL) - { - sysprof_line_visualizer_load_data_async (self, - NULL, - sysprof_line_visualizer_load_data_cb, - NULL); - } - - return G_SOURCE_REMOVE; -} - -static void -sysprof_line_visualizer_queue_reload (SysprofLineVisualizer *self) -{ - SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self); - - g_assert (SYSPROF_IS_LINE_VISUALIZER (self)); - - if (priv->queued_load == 0) - priv->queued_load = g_idle_add_full (G_PRIORITY_LOW, - sysprof_line_visualizer_do_reload, - self, - NULL); -} - -static void -sysprof_line_visualizer_set_reader (SysprofVisualizer *row, - SysprofCaptureReader *reader) -{ - SysprofLineVisualizer *self = (SysprofLineVisualizer *)row; - SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self); - - g_assert (SYSPROF_IS_LINE_VISUALIZER (self)); - - if (priv->reader != reader) - { - if (priv->reader != NULL) - { - sysprof_capture_reader_unref (priv->reader); - priv->reader = NULL; - } - - if (reader != NULL) - priv->reader = sysprof_capture_reader_ref (reader); - - sysprof_line_visualizer_queue_reload (self); - } -} - -static void -sysprof_line_visualizer_finalize (GObject *object) -{ - SysprofLineVisualizer *self = (SysprofLineVisualizer *)object; - SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self); - - g_clear_pointer (&priv->units, g_free); - g_clear_pointer (&priv->lines, g_array_unref); - g_clear_pointer (&priv->cache, point_cache_unref); - g_clear_pointer (&priv->reader, sysprof_capture_reader_unref); - - g_clear_handle_id (&priv->queued_load, g_source_remove); - - G_OBJECT_CLASS (sysprof_line_visualizer_parent_class)->finalize (object); -} - -static void -sysprof_line_visualizer_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - SysprofLineVisualizer *self = SYSPROF_LINE_VISUALIZER (object); - SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self); - - switch (prop_id) - { - case PROP_Y_LOWER: - g_value_set_double (value, priv->y_lower); - break; - - case PROP_Y_UPPER: - g_value_set_double (value, priv->y_upper); - break; - - case PROP_UNITS: - g_value_set_string (value, priv->units); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_line_visualizer_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - SysprofLineVisualizer *self = SYSPROF_LINE_VISUALIZER (object); - SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self); - - switch (prop_id) - { - case PROP_Y_LOWER: - priv->y_lower = g_value_get_double (value); - priv->y_lower_set = TRUE; - gtk_widget_queue_allocate (GTK_WIDGET (self)); - break; - - case PROP_Y_UPPER: - priv->y_upper = g_value_get_double (value); - priv->y_upper_set = TRUE; - gtk_widget_queue_allocate (GTK_WIDGET (self)); - break; - - case PROP_UNITS: - g_free (priv->units); - priv->units = g_value_dup_string (value); - gtk_widget_queue_allocate (GTK_WIDGET (self)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_line_visualizer_class_init (SysprofLineVisualizerClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); - SysprofVisualizerClass *visualizer_class = SYSPROF_VISUALIZER_CLASS (klass); - - object_class->finalize = sysprof_line_visualizer_finalize; - object_class->get_property = sysprof_line_visualizer_get_property; - object_class->set_property = sysprof_line_visualizer_set_property; - - widget_class->snapshot = sysprof_line_visualizer_snapshot; - - visualizer_class->set_reader = sysprof_line_visualizer_set_reader; - - properties [PROP_Y_LOWER] = - g_param_spec_double ("y-lower", - "Y Lower", - "The lowest Y value for the visualizer", - -G_MAXDOUBLE, - G_MAXDOUBLE, - 0.0, - (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - properties [PROP_Y_UPPER] = - g_param_spec_double ("y-upper", - "Y Upper", - "The highest Y value for the visualizer", - -G_MAXDOUBLE, - G_MAXDOUBLE, - 100.0, - (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - properties [PROP_UNITS] = - g_param_spec_string ("units", - "Units", - "The format for units (mHz, Watts, etc)", - NULL, - (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_properties (object_class, N_PROPS, properties); -} - -static void -sysprof_line_visualizer_init (SysprofLineVisualizer *self) -{ - SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self); - - priv->lines = g_array_new (FALSE, FALSE, sizeof (LineInfo)); -} - -void -sysprof_line_visualizer_add_counter (SysprofLineVisualizer *self, - guint counter_id, - const GdkRGBA *color) -{ - SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self); - LineInfo line_info = { 0 }; - - g_assert (SYSPROF_IS_LINE_VISUALIZER (self)); - g_assert (priv->lines != NULL); - - line_info.id = counter_id; - line_info.line_width = 1.0; - line_info.type = SYSPROF_CAPTURE_COUNTER_DOUBLE; - - if (color != NULL) - { - line_info.foreground = *color; - line_info.use_default_style = FALSE; - } - else - { - gdk_rgba_parse (&line_info.foreground, "#000"); - line_info.use_default_style = TRUE; - } - - g_array_append_val (priv->lines, line_info); - - if (SYSPROF_LINE_VISUALIZER_GET_CLASS (self)->counter_added) - SYSPROF_LINE_VISUALIZER_GET_CLASS (self)->counter_added (self, counter_id); - - sysprof_line_visualizer_queue_reload (self); -} - -void -sysprof_line_visualizer_clear (SysprofLineVisualizer *self) -{ - SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self); - - g_return_if_fail (SYSPROF_IS_LINE_VISUALIZER (self)); - - if (priv->lines->len > 0) - g_array_remove_range (priv->lines, 0, priv->lines->len); - - gtk_widget_queue_draw (GTK_WIDGET (self)); -} - -static inline gboolean -contains_id (GArray *ar, - guint id) -{ - for (guint i = 0; i < ar->len; i++) - { - const LineInfo *info = &g_array_index (ar, LineInfo, i); - - if (info->id == id) - return TRUE; - } - - return FALSE; -} - -static inline guint8 -counter_type (LoadData *load, - guint counter_id) -{ - for (guint i = 0; i < load->lines->len; i++) - { - const LineInfo *info = &g_array_index (load->lines, LineInfo, i); - - if (info->id == counter_id) - return info->type; - } - - return SYSPROF_CAPTURE_COUNTER_DOUBLE; -} - -static inline gdouble -calc_x (gint64 lower, - gint64 upper, - gint64 value) -{ - return (gdouble)(value - lower) / (gdouble)(upper - lower); -} - -static inline gdouble -calc_y_double (gdouble lower, - gdouble upper, - gdouble value) -{ - return (value - lower) / (upper - lower); -} - -static inline gdouble -calc_y_int64 (gint64 lower, - gint64 upper, - gint64 value) -{ - return (gdouble)(value - lower) / (gdouble)(upper - lower); -} - -static bool -sysprof_line_visualizer_load_data_frame_cb (const SysprofCaptureFrame *frame, - gpointer user_data) -{ - LoadData *load = user_data; - - g_assert (frame != NULL); - g_assert (frame->type == SYSPROF_CAPTURE_FRAME_CTRSET || - frame->type == SYSPROF_CAPTURE_FRAME_CTRDEF); - g_assert (load != NULL); - - if (frame->type == SYSPROF_CAPTURE_FRAME_CTRSET) - { - const SysprofCaptureCounterSet *set = (SysprofCaptureCounterSet *)frame; - gdouble x = calc_x (load->begin_time, load->end_time, frame->time); - - for (guint i = 0; i < set->n_values; i++) - { - const SysprofCaptureCounterValues *group = &set->values[i]; - - for (guint j = 0; j < G_N_ELEMENTS (group->ids); j++) - { - guint counter_id = group->ids[j]; - - if (counter_id != 0 && contains_id (load->lines, counter_id)) - { - gdouble y; - - if (counter_type (load, counter_id) == SYSPROF_CAPTURE_COUNTER_DOUBLE) - y = calc_y_double (load->y_lower, load->y_upper, group->values[j].vdbl); - else - y = calc_y_int64 (load->y_lower, load->y_upper, group->values[j].v64); - - point_cache_add_point_to_set (load->cache, counter_id, x, y); - } - } - } - } - - return TRUE; -} - -static bool -sysprof_line_visualizer_load_data_range_cb (const SysprofCaptureFrame *frame, - gpointer user_data) -{ - LoadData *load = user_data; - - g_assert (frame != NULL); - g_assert (frame->type == SYSPROF_CAPTURE_FRAME_CTRSET || - frame->type == SYSPROF_CAPTURE_FRAME_CTRDEF); - g_assert (load != NULL); - g_assert (load->y_upper_set == FALSE || - load->y_lower_set == FALSE); - - if (frame->type == SYSPROF_CAPTURE_FRAME_CTRDEF) - { - const SysprofCaptureCounterDefine *def = (SysprofCaptureCounterDefine *)frame; - - for (guint i = 0; i < def->n_counters; i++) - { - const SysprofCaptureCounter *ctr = &def->counters[i]; - - for (guint j = 0; j < load->lines->len; j++) - { - LineInfo *info = &g_array_index (load->lines, LineInfo, j); - - if (info->id == ctr->id) - { - info->type = ctr->type; - break; - } - } - } - } - else if (frame->type == SYSPROF_CAPTURE_FRAME_CTRSET) - { - const SysprofCaptureCounterSet *set = (SysprofCaptureCounterSet *)frame; - - for (guint i = 0; i < set->n_values; i++) - { - const SysprofCaptureCounterValues *group = &set->values[i]; - - for (guint j = 0; j < G_N_ELEMENTS (group->ids); j++) - { - guint counter_id = group->ids[j]; - - if (counter_id != 0 && contains_id (load->lines, counter_id)) - { - gdouble y; - - if (counter_type (load, counter_id) == SYSPROF_CAPTURE_COUNTER_DOUBLE) - y = group->values[j].vdbl; - else - y = group->values[j].v64; - - if (!load->y_upper_set) - load->y_upper = MAX (load->y_upper, y); - - if (!load->y_lower_set) - load->y_lower = MIN (load->y_lower, y); - } - } - } - } - - return TRUE; -} - -static void -sysprof_line_visualizer_load_data_worker (GTask *task, - gpointer source_object, - gpointer task_data, - GCancellable *cancellable) -{ - LoadData *load = task_data; - g_autoptr(GArray) counter_ids = NULL; - - g_assert (G_IS_TASK (task)); - g_assert (SYSPROF_IS_LINE_VISUALIZER (source_object)); - g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); - - counter_ids = g_array_new (FALSE, FALSE, sizeof (guint)); - - for (guint i = 0; i < load->lines->len; i++) - { - const LineInfo *line_info = &g_array_index (load->lines, LineInfo, i); - g_array_append_val (counter_ids, line_info->id); - } - - sysprof_capture_cursor_add_condition (load->cursor, - sysprof_capture_condition_new_where_counter_in (counter_ids->len, - (guint *)(gpointer)counter_ids->data)); - - /* If y boundaries are not set, we need to discover them by scaning the data. */ - if (!load->y_lower_set || !load->y_upper_set) - { - sysprof_capture_cursor_foreach (load->cursor, sysprof_line_visualizer_load_data_range_cb, load); - sysprof_capture_cursor_reset (load->cursor); - - /* Add extra boundary for some space above the graph line */ - if (G_MAXDOUBLE - load->y_upper > (load->y_upper * .25)) - load->y_upper = load->y_upper + ((load->y_upper - load->y_lower) * .25); - } - - sysprof_capture_cursor_foreach (load->cursor, sysprof_line_visualizer_load_data_frame_cb, load); - g_task_return_pointer (task, g_steal_pointer (&load->cache), (GDestroyNotify)point_cache_unref); -} - -static void -sysprof_line_visualizer_load_data_async (SysprofLineVisualizer *self, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self); - g_autoptr(GTask) task = NULL; - LoadData *load; - - g_assert (SYSPROF_IS_LINE_VISUALIZER (self)); - g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); - - task = g_task_new (self, cancellable, callback, user_data); - g_task_set_priority (task, G_PRIORITY_LOW); - g_task_set_source_tag (task, sysprof_line_visualizer_load_data_async); - - if (priv->reader == NULL) - { - g_task_return_new_error (task, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "No data loaded"); - return; - } - - load = g_slice_new0 (LoadData); - load->cache = point_cache_new (); - load->y_lower = priv->y_lower_set ? priv->y_lower : G_MAXDOUBLE; - load->y_upper = priv->y_upper_set ? priv->y_upper : -G_MAXDOUBLE; - load->y_lower_set = priv->y_lower_set; - load->y_upper_set = priv->y_upper_set; - load->begin_time = sysprof_capture_reader_get_start_time (priv->reader); - load->end_time = sysprof_capture_reader_get_end_time (priv->reader); - load->cursor = sysprof_capture_cursor_new (priv->reader); - load->lines = copy_array (priv->lines); - - for (guint i = 0; i < load->lines->len; i++) - { - const LineInfo *line_info = &g_array_index (load->lines, LineInfo, i); - - point_cache_add_set (load->cache, line_info->id); - } - - g_task_set_task_data (task, load, load_data_free); - g_task_run_in_thread (task, sysprof_line_visualizer_load_data_worker); -} - -static PointCache * -sysprof_line_visualizer_load_data_finish (SysprofLineVisualizer *self, - GAsyncResult *result, - GError **error) -{ - SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self); - LoadData *state; - - g_assert (SYSPROF_IS_LINE_VISUALIZER (self)); - g_assert (G_IS_TASK (result)); - - state = g_task_get_task_data (G_TASK (result)); - - if (!priv->y_lower_set && priv->y_lower != state->y_lower) - { - priv->y_lower = state->y_lower; - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_Y_LOWER]); - } - - if (!priv->y_upper_set && priv->y_upper != state->y_upper) - { - priv->y_upper = state->y_upper; - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_Y_UPPER]); - } - - return g_task_propagate_pointer (G_TASK (result), error); -} - -void -sysprof_line_visualizer_set_line_width (SysprofLineVisualizer *self, - guint counter_id, - gdouble width) -{ - SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self); - - g_return_if_fail (SYSPROF_IS_LINE_VISUALIZER (self)); - - for (guint i = 0; i < priv->lines->len; i++) - { - LineInfo *info = &g_array_index (priv->lines, LineInfo, i); - - if (info->id == counter_id) - { - info->line_width = width; - sysprof_line_visualizer_queue_reload (self); - break; - } - } -} - -void -sysprof_line_visualizer_set_fill (SysprofLineVisualizer *self, - guint counter_id, - const GdkRGBA *color) -{ - SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self); - - g_return_if_fail (SYSPROF_IS_LINE_VISUALIZER (self)); - - for (guint i = 0; i < priv->lines->len; i++) - { - LineInfo *info = &g_array_index (priv->lines, LineInfo, i); - - if (info->id == counter_id) - { - info->fill = !!color; - if (color != NULL) - info->background = *color; - sysprof_line_visualizer_queue_reload (self); - break; - } - } -} - -void -sysprof_line_visualizer_set_dash (SysprofLineVisualizer *self, - guint counter_id, - gboolean use_dash) -{ - SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self); - - g_return_if_fail (SYSPROF_IS_LINE_VISUALIZER (self)); - - for (guint i = 0; i < priv->lines->len; i++) - { - LineInfo *info = &g_array_index (priv->lines, LineInfo, i); - - if (info->id == counter_id) - { - info->use_dash = !!use_dash; - sysprof_line_visualizer_queue_reload (self); - break; - } - } -} diff --git a/src/libsysprof-ui/sysprof-line-visualizer.h b/src/libsysprof-ui/sysprof-line-visualizer.h deleted file mode 100644 index 4fac8855..00000000 --- a/src/libsysprof-ui/sysprof-line-visualizer.h +++ /dev/null @@ -1,57 +0,0 @@ -/* sysprof-line-visualizer.h - * - * Copyright 2016-2019 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-visualizer.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_LINE_VISUALIZER (sysprof_line_visualizer_get_type()) - -G_DECLARE_DERIVABLE_TYPE (SysprofLineVisualizer, sysprof_line_visualizer, SYSPROF, LINE_VISUALIZER, SysprofVisualizer) - -struct _SysprofLineVisualizerClass -{ - SysprofVisualizerClass parent_class; - - void (*counter_added) (SysprofLineVisualizer *self, - guint counter_id); - - /*< private >*/ - gpointer _reserved[16]; -}; - -GtkWidget *sysprof_line_visualizer_new (void); -void sysprof_line_visualizer_clear (SysprofLineVisualizer *self); -void sysprof_line_visualizer_add_counter (SysprofLineVisualizer *self, - guint counter_id, - const GdkRGBA *color); -void sysprof_line_visualizer_set_line_width (SysprofLineVisualizer *self, - guint counter_id, - gdouble width); -void sysprof_line_visualizer_set_fill (SysprofLineVisualizer *self, - guint counter_id, - const GdkRGBA *color); -void sysprof_line_visualizer_set_dash (SysprofLineVisualizer *self, - guint counter_id, - gboolean use_dash); - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-log-model.c b/src/libsysprof-ui/sysprof-log-model.c deleted file mode 100644 index 8d67254f..00000000 --- a/src/libsysprof-ui/sysprof-log-model.c +++ /dev/null @@ -1,422 +0,0 @@ -/* sysprof-log-model.c - * - * Copyright 2019 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 - */ - -#define G_LOG_DOMAIN "sysprof-log-model" - -#include "config.h" - -#include -#include -#include - -#include "sysprof-log-model.h" - -struct _SysprofLogModel -{ - GObject parent_instance; - GStringChunk *chunks; - GArray *items; - gint64 begin_time; -}; - -typedef struct -{ - gint64 time; - const gchar *domain; - const gchar *message; - guint16 severity; -} Item; - -static gint -sysprof_log_model_get_n_columns (GtkTreeModel *model) -{ - return SYSPROF_LOG_MODEL_COLUMN_LAST; -} - -static GType -sysprof_log_model_get_column_type (GtkTreeModel *model, - gint column) -{ - switch (column) - { - case SYSPROF_LOG_MODEL_COLUMN_TIME: - return G_TYPE_INT64; - - case SYSPROF_LOG_MODEL_COLUMN_SEVERITY: - case SYSPROF_LOG_MODEL_COLUMN_DOMAIN: - case SYSPROF_LOG_MODEL_COLUMN_MESSAGE: - case SYSPROF_LOG_MODEL_COLUMN_TIME_STRING: - return G_TYPE_STRING; - - default: - return 0; - } -} - -static GtkTreePath * -sysprof_log_model_get_path (GtkTreeModel *model, - GtkTreeIter *iter) -{ - gint off; - - g_assert (SYSPROF_IS_LOG_MODEL (model)); - g_assert (iter != NULL); - - off = GPOINTER_TO_INT (iter->user_data); - - return gtk_tree_path_new_from_indices (off, -1); -} - -static gboolean -sysprof_log_model_get_iter (GtkTreeModel *model, - GtkTreeIter *iter, - GtkTreePath *path) -{ - SysprofLogModel *self = (SysprofLogModel *)model; - gint off; - - g_assert (SYSPROF_IS_LOG_MODEL (self)); - g_assert (iter != NULL); - g_assert (path != NULL); - - memset (iter, 0, sizeof *iter); - - if (gtk_tree_path_get_depth (path) != 1) - return FALSE; - - off = gtk_tree_path_get_indices (path)[0]; - iter->user_data = GINT_TO_POINTER (off); - - return off >= 0 && off < self->items->len; -} - -static gboolean -sysprof_log_model_iter_next (GtkTreeModel *model, - GtkTreeIter *iter) -{ - SysprofLogModel *self = (SysprofLogModel *)model; - gint off; - - g_assert (SYSPROF_IS_LOG_MODEL (self)); - g_assert (iter != NULL); - - off = GPOINTER_TO_INT (iter->user_data); - off++; - iter->user_data = GINT_TO_POINTER (off); - - return off < self->items->len; -} - -static gboolean -sysprof_log_model_iter_nth_child (GtkTreeModel *model, - GtkTreeIter *iter, - GtkTreeIter *parent, - gint n) -{ - SysprofLogModel *self = (SysprofLogModel *)model; - - g_assert (SYSPROF_IS_LOG_MODEL (self)); - g_assert (iter != NULL); - - if (parent != NULL) - return FALSE; - - iter->user_data = GINT_TO_POINTER (n); - - return n < self->items->len; -} - -static gint -sysprof_log_model_iter_n_children (GtkTreeModel *model, - GtkTreeIter *iter) -{ - SysprofLogModel *self = (SysprofLogModel *)model; - - g_assert (SYSPROF_IS_LOG_MODEL (self)); - - return iter ? 0 : self->items->len; -} - -static gboolean -sysprof_log_model_iter_has_child (GtkTreeModel *model, - GtkTreeIter *iter) -{ - return FALSE; -} - -static GtkTreeModelFlags -sysprof_log_model_get_flags (GtkTreeModel *model) -{ - return GTK_TREE_MODEL_LIST_ONLY; -} - -static void -sysprof_log_model_get_value (GtkTreeModel *model, - GtkTreeIter *iter, - gint column, - GValue *value) -{ - SysprofLogModel *self = (SysprofLogModel *)model; - const Item *item; - - g_assert (SYSPROF_IS_LOG_MODEL (self)); - g_assert (iter != NULL); - g_assert (column < SYSPROF_LOG_MODEL_COLUMN_LAST); - - item = &g_array_index (self->items, Item, GPOINTER_TO_INT (iter->user_data)); - - switch (column) - { - case SYSPROF_LOG_MODEL_COLUMN_TIME_STRING: - { - gint64 offset = item->time - self->begin_time; - gint min = offset / SYSPROF_NSEC_PER_SEC / 60L; - gint seconds = ((offset - (min * SYSPROF_NSEC_PER_SEC)) / SYSPROF_NSEC_PER_SEC) % 60; - gint msec = (offset % SYSPROF_NSEC_PER_SEC) / (SYSPROF_NSEC_PER_SEC / 1000L); - - g_value_init (value, G_TYPE_STRING); - g_value_take_string (value, - g_strdup_printf ("%02d:%02d.%03d", min, seconds, msec)); - } - break; - - case SYSPROF_LOG_MODEL_COLUMN_TIME: - g_value_init (value, G_TYPE_INT64); - g_value_set_int64 (value, item->time); - break; - - case SYSPROF_LOG_MODEL_COLUMN_SEVERITY: - g_value_init (value, G_TYPE_STRING); - switch (item->severity) - { - case G_LOG_LEVEL_MESSAGE: - g_value_set_static_string (value, _("Message")); - break; - case G_LOG_LEVEL_INFO: - g_value_set_static_string (value, _("Info")); - break; - case G_LOG_LEVEL_CRITICAL: - g_value_set_static_string (value, _("Critical")); - break; - case G_LOG_LEVEL_ERROR: - g_value_set_static_string (value, _("Error")); - break; - case G_LOG_LEVEL_DEBUG: - g_value_set_static_string (value, _("Debug")); - break; - case G_LOG_LEVEL_WARNING: - g_value_set_static_string (value, _("Warning")); - break; - default: - g_value_set_static_string (value, ""); - break; - } - break; - - case SYSPROF_LOG_MODEL_COLUMN_DOMAIN: - g_value_init (value, G_TYPE_STRING); - g_value_set_string (value, item->domain); - break; - - case SYSPROF_LOG_MODEL_COLUMN_MESSAGE: - g_value_init (value, G_TYPE_STRING); - g_value_set_string (value, item->message); - break; - - default: - break; - } -} - -static void -tree_model_iface_init (GtkTreeModelIface *iface) -{ - iface->get_n_columns = sysprof_log_model_get_n_columns; - iface->get_column_type = sysprof_log_model_get_column_type; - iface->get_iter = sysprof_log_model_get_iter; - iface->get_path = sysprof_log_model_get_path; - iface->iter_next = sysprof_log_model_iter_next; - iface->iter_n_children = sysprof_log_model_iter_n_children; - iface->iter_nth_child = sysprof_log_model_iter_nth_child; - iface->iter_has_child = sysprof_log_model_iter_has_child; - iface->get_flags = sysprof_log_model_get_flags; - iface->get_value = sysprof_log_model_get_value; -} - -G_DEFINE_TYPE_WITH_CODE (SysprofLogModel, sysprof_log_model, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL, tree_model_iface_init)) - -static void -sysprof_log_model_finalize (GObject *object) -{ - SysprofLogModel *self = (SysprofLogModel *)object; - - g_clear_pointer (&self->items, g_array_unref); - g_clear_pointer (&self->chunks, g_string_chunk_free); - - G_OBJECT_CLASS (sysprof_log_model_parent_class)->finalize (object); -} - -static void -sysprof_log_model_class_init (SysprofLogModelClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->finalize = sysprof_log_model_finalize; -} - -static void -sysprof_log_model_init (SysprofLogModel *self) -{ - self->chunks = g_string_chunk_new (4096*16); - self->items = g_array_new (FALSE, FALSE, sizeof (Item)); -} - -static bool -cursor_foreach_cb (const SysprofCaptureFrame *frame, - gpointer user_data) -{ - SysprofLogModel *self = user_data; - SysprofCaptureLog *log = (SysprofCaptureLog *)frame; - Item item; - - g_assert (SYSPROF_IS_LOG_MODEL (self)); - g_assert (frame->type == SYSPROF_CAPTURE_FRAME_LOG); - - item.time = frame->time; - item.severity = log->severity; - item.domain = g_string_chunk_insert_const (self->chunks, log->domain); - item.message = g_string_chunk_insert_const (self->chunks, log->message); - - g_array_append_val (self->items, item); - - return TRUE; -} - -static gint -item_compare (gconstpointer a, - gconstpointer b) -{ - const Item *ia = a; - const Item *ib = b; - - if (ia->time < ib->time) - return -1; - else if (ia->time > ib->time) - return 1; - else - return 0; -} - -static void -sysprof_log_model_new_worker (GTask *task, - gpointer source_object, - gpointer task_data, - GCancellable *cancellable) -{ - g_autoptr(SysprofLogModel) self = NULL; - SysprofCaptureCursor *cursor = task_data; - SysprofCaptureReader *reader; - - g_assert (G_IS_TASK (task)); - g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); - - self = g_object_new (SYSPROF_TYPE_LOG_MODEL, NULL); - - reader = sysprof_capture_cursor_get_reader (cursor); - self->begin_time = sysprof_capture_reader_get_start_time (reader); - - sysprof_capture_cursor_foreach (cursor, cursor_foreach_cb, self); - g_array_sort (self->items, item_compare); - - g_task_return_pointer (task, g_steal_pointer (&self), g_object_unref); -} - -static void -sysprof_log_model_selection_foreach_cb (SysprofSelection *selection, - gint64 begin, - gint64 end, - gpointer user_data) -{ - SysprofCaptureCondition **condition = user_data; - SysprofCaptureCondition *c; - - g_assert (SYSPROF_IS_SELECTION (selection)); - g_assert (condition != NULL); - - c = sysprof_capture_condition_new_where_time_between (begin, end); - - if (*condition != NULL) - *condition = sysprof_capture_condition_new_or (g_steal_pointer (&c), - g_steal_pointer (condition)); - else - *condition = g_steal_pointer (&c); -} - -void -sysprof_log_model_new_async (SysprofCaptureReader *reader, - SysprofSelection *selection, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - static const SysprofCaptureFrameType types[] = { - SYSPROF_CAPTURE_FRAME_LOG, - }; - g_autoptr(SysprofCaptureCursor) cursor = NULL; - SysprofCaptureCondition *c; - g_autoptr(GTask) task = NULL; - - g_return_if_fail (reader != NULL); - g_return_if_fail (!selection || SYSPROF_IS_SELECTION (selection)); - g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); - - cursor = sysprof_capture_cursor_new (reader); - c = sysprof_capture_condition_new_where_type_in (G_N_ELEMENTS (types), types); - - if (selection) - { - SysprofCaptureCondition *condition = NULL; - - sysprof_selection_foreach (selection, - sysprof_log_model_selection_foreach_cb, - &condition); - if (condition) - c = sysprof_capture_condition_new_and (c, g_steal_pointer (&condition)); - } - - sysprof_capture_cursor_add_condition (cursor, g_steal_pointer (&c)); - - task = g_task_new (NULL, cancellable, callback, user_data); - g_task_set_source_tag (task, sysprof_log_model_new_async); - g_task_set_task_data (task, - g_steal_pointer (&cursor), - (GDestroyNotify) sysprof_capture_cursor_unref); - g_task_run_in_thread (task, sysprof_log_model_new_worker); -} - -SysprofLogModel * -sysprof_log_model_new_finish (GAsyncResult *result, - GError **error) -{ - g_return_val_if_fail (G_IS_TASK (result), NULL); - - return g_task_propagate_pointer (G_TASK (result), error); -} diff --git a/src/libsysprof-ui/sysprof-log-model.h b/src/libsysprof-ui/sysprof-log-model.h deleted file mode 100644 index 7edb25fa..00000000 --- a/src/libsysprof-ui/sysprof-log-model.h +++ /dev/null @@ -1,49 +0,0 @@ -/* sysprof-log-model.h - * - * Copyright 2019 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 - -typedef enum -{ - SYSPROF_LOG_MODEL_COLUMN_TIME, - SYSPROF_LOG_MODEL_COLUMN_SEVERITY, - SYSPROF_LOG_MODEL_COLUMN_DOMAIN, - SYSPROF_LOG_MODEL_COLUMN_MESSAGE, - SYSPROF_LOG_MODEL_COLUMN_TIME_STRING, - SYSPROF_LOG_MODEL_COLUMN_LAST -} SysprofLogModelColumn; - -#define SYSPROF_TYPE_LOG_MODEL (sysprof_log_model_get_type()) - -G_DECLARE_FINAL_TYPE (SysprofLogModel, sysprof_log_model, SYSPROF, LOG_MODEL, GObject) - -void sysprof_log_model_new_async (SysprofCaptureReader *reader, - SysprofSelection *selection, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -SysprofLogModel *sysprof_log_model_new_finish (GAsyncResult *result, - GError **error); - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-logs-aid.c b/src/libsysprof-ui/sysprof-logs-aid.c deleted file mode 100644 index 8807cbe5..00000000 --- a/src/libsysprof-ui/sysprof-logs-aid.c +++ /dev/null @@ -1,237 +0,0 @@ -/* sysprof-logs-aid.c - * - * Copyright 2019 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 - */ - -#define G_LOG_DOMAIN "sysprof-logs-aid" - -#include "config.h" - -#include - -#include "sysprof-color-cycle.h" -#include "sysprof-logs-aid.h" -#include "sysprof-logs-page.h" -#include "sysprof-mark-visualizer.h" - -struct _SysprofLogsAid -{ - SysprofAid parent_instance; -}; - -typedef struct -{ - SysprofDisplay *display; - SysprofCaptureCursor *cursor; - GArray *log_marks; -} Present; - -G_DEFINE_TYPE (SysprofLogsAid, sysprof_logs_aid, SYSPROF_TYPE_AID) - -static void -present_free (gpointer data) -{ - Present *p = data; - - g_clear_pointer (&p->log_marks, g_array_unref); - g_clear_pointer (&p->cursor, sysprof_capture_cursor_unref); - g_clear_object (&p->display); - g_slice_free (Present, p); -} - -static void -on_group_activated_cb (SysprofVisualizerGroup *group, - SysprofPage *page) -{ - SysprofDisplay *display; - - g_assert (SYSPROF_IS_VISUALIZER_GROUP (group)); - g_assert (SYSPROF_IS_PAGE (page)); - - display = SYSPROF_DISPLAY (gtk_widget_get_ancestor (GTK_WIDGET (page), SYSPROF_TYPE_DISPLAY)); - sysprof_display_set_visible_page (display, page); -} - -/** - * sysprof_logs_aid_new: - * - * Create a new #SysprofLogsAid. - * - * Returns: (transfer full): a newly created #SysprofLogsAid - */ -SysprofAid * -sysprof_logs_aid_new (void) -{ - return g_object_new (SYSPROF_TYPE_LOGS_AID, NULL); -} - -static bool -find_marks_cb (const SysprofCaptureFrame *frame, - gpointer user_data) -{ - Present *p = user_data; - - g_assert (frame != NULL); - g_assert (p != NULL); - - if (frame->type == SYSPROF_CAPTURE_FRAME_LOG) - { - SysprofMarkTimeSpan span = { frame->time, frame->time }; - g_array_append_val (p->log_marks, span); - } - - return TRUE; -} - -static gint -compare_span (const SysprofMarkTimeSpan *a, - const SysprofMarkTimeSpan *b) -{ - if (a->kind < b->kind) - return -1; - - if (b->kind < a->kind) - return 1; - - if (a->begin < b->begin) - return -1; - - if (b->begin < a->begin) - return 1; - - if (b->end > a->end) - return -1; - - return 0; -} - -static void -sysprof_logs_aid_present_worker (GTask *task, - gpointer source_object, - gpointer task_data, - GCancellable *cancellable) -{ - Present *p = task_data; - - g_assert (G_IS_TASK (task)); - g_assert (p != NULL); - g_assert (SYSPROF_IS_DISPLAY (p->display)); - g_assert (p->cursor != NULL); - g_assert (SYSPROF_IS_LOGS_AID (source_object)); - - sysprof_capture_cursor_foreach (p->cursor, find_marks_cb, p); - g_array_sort (p->log_marks, (GCompareFunc)compare_span); - - g_task_return_boolean (task, TRUE); -} - -static void -sysprof_logs_aid_present_async (SysprofAid *aid, - SysprofCaptureReader *reader, - SysprofDisplay *display, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - static const SysprofCaptureFrameType logs[] = { - SYSPROF_CAPTURE_FRAME_LOG, - }; - SysprofLogsAid *self = (SysprofLogsAid *)aid; - g_autoptr(GTask) task = NULL; - Present p = {0}; - - g_assert (SYSPROF_IS_LOGS_AID (self)); - - p.display = g_object_ref (display); - p.log_marks = g_array_new (FALSE, FALSE, sizeof (SysprofMarkTimeSpan)); - p.cursor = sysprof_capture_cursor_new (reader); - sysprof_capture_cursor_add_condition (p.cursor, - sysprof_capture_condition_new_where_type_in (1, logs)); - - task = g_task_new (self, cancellable, callback, user_data); - g_task_set_source_tag (task, sysprof_logs_aid_present_async); - g_task_set_task_data (task, - g_slice_dup (Present, &p), - present_free); - g_task_run_in_thread (task, sysprof_logs_aid_present_worker); -} - -static gboolean -sysprof_logs_aid_present_finish (SysprofAid *aid, - GAsyncResult *result, - GError **error) -{ - Present *p; - - g_assert (SYSPROF_IS_LOGS_AID (aid)); - g_assert (G_IS_TASK (result)); - - p = g_task_get_task_data (G_TASK (result)); - - if (p->log_marks->len > 0) - { - g_autoptr(GHashTable) items = NULL; - SysprofVisualizerGroup *group; - SysprofVisualizer *marks; - SysprofPage *page; - - items = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, - (GDestroyNotify) g_array_unref); - g_hash_table_insert (items, g_strdup (_("Logs")), g_array_ref (p->log_marks)); - - group = g_object_new (SYSPROF_TYPE_VISUALIZER_GROUP, - "can-focus", TRUE, - "title", _("Logs"), - "visible", TRUE, - NULL); - - marks = sysprof_mark_visualizer_new (items); - sysprof_visualizer_set_title (marks, _("Logs")); - gtk_widget_show (GTK_WIDGET (marks)); - sysprof_visualizer_group_insert (group, marks, 0, FALSE); - sysprof_display_add_group (p->display, group); - - page = g_object_new (SYSPROF_TYPE_LOGS_PAGE, - "title", _("Logs"), - "visible", TRUE, - NULL); - sysprof_display_add_page (p->display, page); - - g_signal_connect_object (group, - "group-activated", - G_CALLBACK (on_group_activated_cb), - page, - 0); - } - - return g_task_propagate_boolean (G_TASK (result), error); -} - -static void -sysprof_logs_aid_class_init (SysprofLogsAidClass *klass) -{ - SysprofAidClass *aid_class = SYSPROF_AID_CLASS (klass); - - aid_class->present_async = sysprof_logs_aid_present_async; - aid_class->present_finish = sysprof_logs_aid_present_finish; -} - -static void -sysprof_logs_aid_init (SysprofLogsAid *self) -{ -} diff --git a/src/libsysprof-ui/sysprof-logs-aid.h b/src/libsysprof-ui/sysprof-logs-aid.h deleted file mode 100644 index 2f2d13ad..00000000 --- a/src/libsysprof-ui/sysprof-logs-aid.h +++ /dev/null @@ -1,33 +0,0 @@ -/* sysprof-logs-aid.h - * - * Copyright 2019 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-aid.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_LOGS_AID (sysprof_logs_aid_get_type()) - -G_DECLARE_FINAL_TYPE (SysprofLogsAid, sysprof_logs_aid, SYSPROF, LOGS_AID, SysprofAid) - -SysprofAid *sysprof_logs_aid_new (void); - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-logs-page.c b/src/libsysprof-ui/sysprof-logs-page.c deleted file mode 100644 index f2b46af8..00000000 --- a/src/libsysprof-ui/sysprof-logs-page.c +++ /dev/null @@ -1,116 +0,0 @@ -/* sysprof-logs-page.c - * - * Copyright 2019 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 - */ - -#define G_LOG_DOMAIN "sysprof-logs-page" - -#include "config.h" - -#include "sysprof-log-model.h" -#include "sysprof-logs-page.h" - -struct _SysprofLogsPage -{ - SysprofPage parent_instance; - - /* Template Widgets */ - GtkTreeView *tree_view; -}; - -G_DEFINE_TYPE (SysprofLogsPage, sysprof_logs_page, SYSPROF_TYPE_PAGE) - -static void -sysprof_logs_page_load_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - SysprofLogsPage *self; - g_autoptr(SysprofLogModel) model = NULL; - g_autoptr(GError) error = NULL; - g_autoptr(GTask) task = user_data; - - g_assert (G_IS_ASYNC_RESULT (result)); - g_assert (G_IS_TASK (task)); - - if (!(model = sysprof_log_model_new_finish (result, &error))) - g_task_return_error (task, g_steal_pointer (&error)); - else - g_task_return_boolean (task, TRUE); - - self = g_task_get_source_object (task); - - gtk_tree_view_set_model (self->tree_view, GTK_TREE_MODEL (model)); -} - -static void -sysprof_logs_page_load_async (SysprofPage *page, - SysprofCaptureReader *reader, - SysprofSelection *selection, - SysprofCaptureCondition *filter, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - SysprofLogsPage *self = (SysprofLogsPage *)page; - g_autoptr(GTask) task = NULL; - - g_assert (SYSPROF_IS_LOGS_PAGE (self)); - g_assert (reader != NULL); - g_assert (!selection || SYSPROF_IS_SELECTION (selection)); - g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); - - task = g_task_new (self, cancellable, callback, user_data); - g_task_set_source_tag (task, sysprof_logs_page_load_async); - - sysprof_log_model_new_async (reader, - selection, - cancellable, - sysprof_logs_page_load_cb, - g_steal_pointer (&task)); -} - -static gboolean -sysprof_logs_page_load_finish (SysprofPage *page, - GAsyncResult *result, - GError **error) -{ - g_assert (SYSPROF_IS_LOGS_PAGE (page)); - g_assert (G_IS_TASK (result)); - - return g_task_propagate_boolean (G_TASK (result), error); -} - -static void -sysprof_logs_page_class_init (SysprofLogsPageClass *klass) -{ - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); - SysprofPageClass *page_class = SYSPROF_PAGE_CLASS (klass); - - page_class->load_async = sysprof_logs_page_load_async; - page_class->load_finish = sysprof_logs_page_load_finish; - - gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/sysprof/ui/sysprof-logs-page.ui"); - gtk_widget_class_bind_template_child (widget_class, SysprofLogsPage, tree_view); -} - -static void -sysprof_logs_page_init (SysprofLogsPage *self) -{ - gtk_widget_init_template (GTK_WIDGET (self)); -} diff --git a/src/libsysprof-ui/sysprof-logs-page.h b/src/libsysprof-ui/sysprof-logs-page.h deleted file mode 100644 index 97bb2455..00000000 --- a/src/libsysprof-ui/sysprof-logs-page.h +++ /dev/null @@ -1,31 +0,0 @@ -/* sysprof-logs-page.h - * - * Copyright 2019 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-page.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_LOGS_PAGE (sysprof_logs_page_get_type()) - -G_DECLARE_FINAL_TYPE (SysprofLogsPage, sysprof_logs_page, SYSPROF, LOGS_PAGE, SysprofPage) - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-logs-page.ui b/src/libsysprof-ui/sysprof-logs-page.ui deleted file mode 100644 index 2f4d565b..00000000 --- a/src/libsysprof-ui/sysprof-logs-page.ui +++ /dev/null @@ -1,73 +0,0 @@ - - - - diff --git a/src/libsysprof-ui/sysprof-mark-detail.c b/src/libsysprof-ui/sysprof-mark-detail.c deleted file mode 100644 index d0899149..00000000 --- a/src/libsysprof-ui/sysprof-mark-detail.c +++ /dev/null @@ -1,208 +0,0 @@ -/* sysprof-mark-detail.c - * - * Copyright 2022 Corentin Noël - * - * 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 - */ - -#define G_LOG_DOMAIN "sysprof-mark-detail" - -#include "config.h" - -#include - -#include "sysprof-mark-detail.h" - -struct _SysprofMarkDetail -{ - GObject parent_instance; - - gchar *label; - gint64 min; - gint64 max; - gint64 average; - gint64 hits; -}; - -G_DEFINE_TYPE (SysprofMarkDetail, sysprof_mark_detail, G_TYPE_OBJECT) - -enum { - PROP_0, - PROP_LABEL, - PROP_MIN, - PROP_MAX, - PROP_AVERAGE, - PROP_HITS, - N_PROPS -}; - -static GParamSpec *properties [N_PROPS]; - -/** - * sysprof_mark_detail_new: - * - * Create a new #SysprofMarkDetail. - * - * Returns: (transfer full): a newly created #SysprofMarkDetail - */ -SysprofMarkDetail * -sysprof_mark_detail_new (const gchar *mark, - gint64 min, - gint64 max, - gint64 avg, - gint64 hits) -{ - return g_object_new (SYSPROF_TYPE_MARK_DETAIL, - "label", mark, - "min", min, - "max", max, - "average", avg, - "hits", hits, - NULL); -} - -static void -sysprof_mark_detail_finalize (GObject *object) -{ - SysprofMarkDetail *self = (SysprofMarkDetail *)object; - - g_clear_pointer (&self->label, g_free); - - G_OBJECT_CLASS (sysprof_mark_detail_parent_class)->finalize (object); -} - -static void -sysprof_mark_detail_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - SysprofMarkDetail *self = SYSPROF_MARK_DETAIL(object); - - switch (prop_id) - { - case PROP_LABEL: - g_value_set_string (value, self->label); - break; - - case PROP_MIN: - g_value_set_int64 (value, self->min); - break; - - case PROP_MAX: - g_value_set_int64 (value, self->max); - break; - - case PROP_AVERAGE: - g_value_set_int64 (value, self->average); - break; - - case PROP_HITS: - g_value_set_int64 (value, self->hits); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_mark_detail_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - SysprofMarkDetail *self = SYSPROF_MARK_DETAIL(object); - - switch (prop_id) - { - case PROP_LABEL: - g_assert(self->label == NULL); - self->label = g_value_dup_string (value); - break; - - case PROP_MIN: - self->min = g_value_get_int64 (value); - break; - - case PROP_MAX: - self->max = g_value_get_int64 (value); - break; - - case PROP_AVERAGE: - self->average = g_value_get_int64 (value); - break; - - case PROP_HITS: - self->hits = g_value_get_int64 (value); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_mark_detail_class_init (SysprofMarkDetailClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->finalize = sysprof_mark_detail_finalize; - object_class->get_property = sysprof_mark_detail_get_property; - object_class->set_property = sysprof_mark_detail_set_property; - - properties [PROP_LABEL] = - g_param_spec_string ("label", - "Label", - "The label of the mark", - NULL, - (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); - - properties [PROP_MIN] = - g_param_spec_int64 ("min", - "Min", - "The minimal timespan", - 0, G_MAXINT64, 0, - (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); - - properties [PROP_MAX] = - g_param_spec_int64 ("max", - "max", - "The maximal timespan", - 0, G_MAXINT64, 0, - (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); - - properties [PROP_AVERAGE] = - g_param_spec_int64 ("average", - "Average", - "The average timespan", - 0, G_MAXINT64, 0, - (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); - - properties [PROP_HITS] = - g_param_spec_int64 ("hits", - "Hits", - "The number of hits", - 0, G_MAXINT64, 0, - (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_properties (object_class, N_PROPS, properties); -} - -static void -sysprof_mark_detail_init (SysprofMarkDetail *self) -{ -} diff --git a/src/libsysprof-ui/sysprof-mark-detail.h b/src/libsysprof-ui/sysprof-mark-detail.h deleted file mode 100644 index 35b292e7..00000000 --- a/src/libsysprof-ui/sysprof-mark-detail.h +++ /dev/null @@ -1,37 +0,0 @@ -/* sysprof-mark-detail.h - * - * Copyright 2022 Corentin Noël - * - * 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_MARK_DETAIL (sysprof_mark_detail_get_type()) - -G_DECLARE_FINAL_TYPE (SysprofMarkDetail, sysprof_mark_detail, SYSPROF, MARK_DETAIL, GObject) - -SysprofMarkDetail *sysprof_mark_detail_new (const gchar *mark, - gint64 min, - gint64 max, - gint64 avg, - gint64 hits); - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-mark-visualizer.c b/src/libsysprof-ui/sysprof-mark-visualizer.c deleted file mode 100644 index ab8a67f9..00000000 --- a/src/libsysprof-ui/sysprof-mark-visualizer.c +++ /dev/null @@ -1,276 +0,0 @@ -/* sysprof-mark-visualizer.c - * - * Copyright 2018-2019 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 - */ - -#define G_LOG_DOMAIN "sysprof-mark-visualizer" - -#include "config.h" - -#include "sysprof-mark-visualizer.h" - -#define RECT_HEIGHT (4) -#define RECT_MIN_WIDTH (3) -#define RECT_OVERLAP (-1) - -struct _SysprofMarkVisualizer -{ - SysprofVisualizer parent_instance; - GHashTable *spans_by_group; - GHashTable *rgba_by_group; - GHashTable *rgba_by_kind; - GHashTable *row_by_kind; - guint x_is_dirty : 1; -}; - -G_DEFINE_TYPE (SysprofMarkVisualizer, sysprof_mark_visualizer, SYSPROF_TYPE_VISUALIZER) - -static void -reset_positions (SysprofMarkVisualizer *self) -{ - g_assert (SYSPROF_IS_MARK_VISUALIZER (self)); - - self->x_is_dirty = TRUE; - gtk_widget_queue_draw (GTK_WIDGET (self)); -} - -SysprofVisualizer * -sysprof_mark_visualizer_new (GHashTable *groups) -{ - SysprofMarkVisualizer *self; - guint n_items; - gint height; - - g_return_val_if_fail (groups != NULL, NULL); - - self = g_object_new (SYSPROF_TYPE_MARK_VISUALIZER, NULL); - self->spans_by_group = g_hash_table_ref (groups); - - reset_positions (self); - - n_items = g_hash_table_size (groups); - height = MAX (35, n_items * (RECT_HEIGHT - RECT_OVERLAP)); - gtk_widget_set_size_request (GTK_WIDGET (self), -1, height); - - return SYSPROF_VISUALIZER (g_steal_pointer (&self)); -} - -static void -sysprof_mark_visualizer_snapshot (GtkWidget *widget, - GtkSnapshot *snapshot) -{ - SysprofMarkVisualizer *self = (SysprofMarkVisualizer *)widget; - SysprofVisualizer *vis = (SysprofVisualizer *)widget; - static const GdkRGBA black = {0,0,0,1}; - const GdkRGBA *rgba = &black; - GHashTableIter iter; - GtkAllocation alloc; - gpointer k, v; - int n_groups = 0; - int y = 0; - - g_assert (SYSPROF_IS_MARK_VISUALIZER (self)); - g_assert (snapshot != NULL); - - GTK_WIDGET_CLASS (sysprof_mark_visualizer_parent_class)->snapshot (widget, snapshot); - - if (self->spans_by_group == NULL) - return; - - gtk_widget_get_allocation (widget, &alloc); - - /* Pre-calculate all time slots so we can join later */ - if (self->x_is_dirty) - { - g_hash_table_iter_init (&iter, self->spans_by_group); - while (g_hash_table_iter_next (&iter, &k, &v)) - { - const GArray *spans = v; - - for (guint i = 0; i < spans->len; i++) - { - SysprofMarkTimeSpan *span = &g_array_index (spans, SysprofMarkTimeSpan, i); - - span->x = sysprof_visualizer_get_x_for_time (vis, span->begin); - span->x2 = sysprof_visualizer_get_x_for_time (vis, span->end); - } - } - - self->x_is_dirty = FALSE; - } - - n_groups = g_hash_table_size (self->spans_by_group); - - g_hash_table_iter_init (&iter, self->spans_by_group); - while (g_hash_table_iter_next (&iter, &k, &v)) - { - SysprofMarkTimeSpan *span; - const gchar *group = k; - const GArray *spans = v; - const GdkRGBA *kindrgba; - const GdkRGBA *grouprgba; - - if ((grouprgba = g_hash_table_lookup (self->rgba_by_group, group))) - rgba = grouprgba; - - for (guint i = 0; i < spans->len; i++) - { - gint x1, x2; - - span = &g_array_index (spans, SysprofMarkTimeSpan, i); - - if (n_groups == 1) - { - rgba = &black; - if ((kindrgba = g_hash_table_lookup (self->rgba_by_kind, GUINT_TO_POINTER (span->kind)))) - rgba = kindrgba; - else if ((grouprgba = g_hash_table_lookup (self->rgba_by_group, group))) - rgba = grouprgba; - } - - x1 = span->x; - x2 = x1 + RECT_MIN_WIDTH; - - if (span->x2 > x2) - x2 = span->x2; - - /* If we are limited to one group, we might need to get the row - * height for the kind of span this is. - */ - if (n_groups == 1) - { - gint row = GPOINTER_TO_INT (g_hash_table_lookup (self->row_by_kind, GUINT_TO_POINTER (span->kind))); - y = row * (RECT_HEIGHT - RECT_OVERLAP); - } - - for (guint j = i + 1; j < spans->len; j++) - { - const SysprofMarkTimeSpan *next = &g_array_index (spans, SysprofMarkTimeSpan, j); - - /* Don't join this if we are about to draw a different kind */ - if (n_groups == 1 && next->kind != span->kind) - break; - - if (next->x <= x2) - { - x2 = MAX (x2, next->x2); - i++; - continue; - } - - break; - } - - gtk_snapshot_append_color (snapshot, rgba, &GRAPHENE_RECT_INIT (x1, y, x2 - x1, RECT_HEIGHT)); - } - - y += RECT_HEIGHT + RECT_OVERLAP; - } -} - -static void -sysprof_mark_visualizer_size_allocate (GtkWidget *widget, - int width, - int height, - int baseline) -{ - SysprofMarkVisualizer *self = (SysprofMarkVisualizer *)widget; - - g_assert (SYSPROF_IS_MARK_VISUALIZER (self)); - - GTK_WIDGET_CLASS (sysprof_mark_visualizer_parent_class)->size_allocate (widget, width, height, baseline); - - reset_positions (self); -} - -static void -sysprof_mark_visualizer_finalize (GObject *object) -{ - SysprofMarkVisualizer *self = (SysprofMarkVisualizer *)object; - - g_clear_pointer (&self->spans_by_group, g_hash_table_unref); - g_clear_pointer (&self->rgba_by_group, g_hash_table_unref); - g_clear_pointer (&self->rgba_by_kind, g_hash_table_unref); - g_clear_pointer (&self->row_by_kind, g_hash_table_unref); - - G_OBJECT_CLASS (sysprof_mark_visualizer_parent_class)->finalize (object); -} - -static void -sysprof_mark_visualizer_class_init (SysprofMarkVisualizerClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); - - object_class->finalize = sysprof_mark_visualizer_finalize; - - widget_class->snapshot = sysprof_mark_visualizer_snapshot; - widget_class->size_allocate = sysprof_mark_visualizer_size_allocate; -} - -static void -sysprof_mark_visualizer_init (SysprofMarkVisualizer *self) -{ - self->rgba_by_kind = g_hash_table_new_full (NULL, NULL, NULL, g_free); - self->row_by_kind = g_hash_table_new (NULL, NULL); - self->rgba_by_group = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); -} - -void -sysprof_mark_visualizer_set_group_rgba (SysprofMarkVisualizer *self, - const gchar *group, - const GdkRGBA *rgba) -{ - g_return_if_fail (SYSPROF_IS_MARK_VISUALIZER (self)); - g_return_if_fail (group != NULL); - - g_hash_table_insert (self->rgba_by_group, - g_strdup (group), - g_memdup2 (rgba, sizeof *rgba)); -} - -void -sysprof_mark_visualizer_set_kind_rgba (SysprofMarkVisualizer *self, - GHashTable *rgba_by_kind) -{ - g_return_if_fail (SYSPROF_IS_MARK_VISUALIZER (self)); - - if (rgba_by_kind != self->rgba_by_kind) - { - g_hash_table_remove_all (self->row_by_kind); - - g_clear_pointer (&self->rgba_by_kind, g_hash_table_unref); - - if (rgba_by_kind) - { - GHashTableIter iter; - guint row = 0; - gpointer k; - - self->rgba_by_kind = g_hash_table_ref (rgba_by_kind); - - g_hash_table_iter_init (&iter, rgba_by_kind); - while (g_hash_table_iter_next (&iter, &k, NULL)) - g_hash_table_insert (self->row_by_kind, k, GUINT_TO_POINTER (row++)); - - gtk_widget_set_size_request (GTK_WIDGET (self), - -1, - MAX (35, row * (RECT_HEIGHT - RECT_OVERLAP))); - } - } -} diff --git a/src/libsysprof-ui/sysprof-mark-visualizer.h b/src/libsysprof-ui/sysprof-mark-visualizer.h deleted file mode 100644 index 36327e13..00000000 --- a/src/libsysprof-ui/sysprof-mark-visualizer.h +++ /dev/null @@ -1,47 +0,0 @@ -/* sysprof-mark-visualizer.h - * - * Copyright 2018-2019 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-visualizer.h" - -G_BEGIN_DECLS - -typedef struct -{ - gint64 begin; - gint64 end; - guint kind; - gint x; - gint x2; -} SysprofMarkTimeSpan; - -#define SYSPROF_TYPE_MARK_VISUALIZER (sysprof_mark_visualizer_get_type()) - -G_DECLARE_FINAL_TYPE (SysprofMarkVisualizer, sysprof_mark_visualizer, SYSPROF, MARK_VISUALIZER, SysprofVisualizer) - -SysprofVisualizer *sysprof_mark_visualizer_new (GHashTable *groups); -void sysprof_mark_visualizer_set_group_rgba (SysprofMarkVisualizer *self, - const gchar *group, - const GdkRGBA *rgba); -void sysprof_mark_visualizer_set_kind_rgba (SysprofMarkVisualizer *self, - GHashTable *rgba_by_kind); - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-marks-aid.c b/src/libsysprof-ui/sysprof-marks-aid.c deleted file mode 100644 index 4361dfab..00000000 --- a/src/libsysprof-ui/sysprof-marks-aid.c +++ /dev/null @@ -1,490 +0,0 @@ -/* sysprof-marks-aid.c - * - * Copyright 2019 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 - */ - -#define G_LOG_DOMAIN "sysprof-marks-aid" - -#include "config.h" - -#include - -#include "sysprof-color-cycle.h" -#include "sysprof-marks-aid.h" -#include "sysprof-marks-page.h" -#include "sysprof-mark-visualizer.h" - -struct _SysprofMarksAid -{ - SysprofAid parent_instance; -}; - -typedef struct -{ - SysprofDisplay *display; - SysprofCaptureCursor *cursor; - GHashTable *categories; - GHashTable *kinds; - guint last_kind; - guint has_marks : 1; -} Present; - -G_DEFINE_TYPE (SysprofMarksAid, sysprof_marks_aid, SYSPROF_TYPE_AID) - -static void -rgb_to_hls (gdouble *r, - gdouble *g, - gdouble *b) -{ - gdouble min; - gdouble max; - gdouble red; - gdouble green; - gdouble blue; - gdouble h, l, s; - gdouble delta; - - red = *r; - green = *g; - blue = *b; - if (red > green) - { - if (red > blue) - max = red; - else - max = blue; - if (green < blue) - min = green; - else - min = blue; - } - else - { - if (green > blue) - max = green; - else - max = blue; - if (red < blue) - min = red; - else - min = blue; - } - l = (max + min) / 2; - s = 0; - h = 0; - if (max != min) - { - if (l <= 0.5) - s = (max - min) / (max + min); - else - s = (max - min) / (2 - max - min); - delta = max - min; - if (red == max) - h = (green - blue) / delta; - else if (green == max) - h = 2 + (blue - red) / delta; - else if (blue == max) - h = 4 + (red - green) / delta; - h *= 60; - if (h < 0.0) - h += 360; - } - *r = h; - *g = l; - *b = s; -} - -static void -hls_to_rgb (gdouble *h, - gdouble *l, - gdouble *s) -{ - gdouble hue; - gdouble lightness; - gdouble saturation; - gdouble m1, m2; - gdouble r, g, b; - - lightness = *l; - saturation = *s; - if (lightness <= 0.5) - m2 = lightness * (1 + saturation); - else - m2 = lightness + saturation - lightness * saturation; - m1 = 2 * lightness - m2; - if (saturation == 0) - { - *h = lightness; - *l = lightness; - *s = lightness; - } - else - { - hue = *h + 120; - while (hue > 360) - hue -= 360; - while (hue < 0) - hue += 360; - if (hue < 60) - r = m1 + (m2 - m1) * hue / 60; - else if (hue < 180) - r = m2; - else if (hue < 240) - r = m1 + (m2 - m1) * (240 - hue) / 60; - else - r = m1; - hue = *h; - while (hue > 360) - hue -= 360; - while (hue < 0) - hue += 360; - if (hue < 60) - g = m1 + (m2 - m1) * hue / 60; - else if (hue < 180) - g = m2; - else if (hue < 240) - g = m1 + (m2 - m1) * (240 - hue) / 60; - else - g = m1; - hue = *h - 120; - while (hue > 360) - hue -= 360; - while (hue < 0) - hue += 360; - if (hue < 60) - b = m1 + (m2 - m1) * hue / 60; - else if (hue < 180) - b = m2; - else if (hue < 240) - b = m1 + (m2 - m1) * (240 - hue) / 60; - else - b = m1; - *h = r; - *l = g; - *s = b; - } -} - -static void -rgba_shade (const GdkRGBA *rgba, - GdkRGBA *dst, - gdouble k) -{ - gdouble red; - gdouble green; - gdouble blue; - - red = rgba->red; - green = rgba->green; - blue = rgba->blue; - - rgb_to_hls (&red, &green, &blue); - - green *= k; - - if (green > 1.0) - green = 1.0; - else if (green < 0.0) - green = 0.0; - - blue *= k; - - if (blue > 1.0) - blue = 1.0; - else if (blue < 0.0) - blue = 0.0; - - hls_to_rgb (&red, &green, &blue); - - dst->red = red; - dst->green = green; - dst->blue = blue; - dst->alpha = rgba->alpha; -} - -static void -present_free (gpointer data) -{ - Present *p = data; - - g_clear_pointer (&p->categories, g_hash_table_unref); - g_clear_pointer (&p->kinds, g_hash_table_unref); - g_clear_pointer (&p->cursor, sysprof_capture_cursor_unref); - g_clear_object (&p->display); - g_slice_free (Present, p); -} - -static void -on_group_activated_cb (SysprofVisualizerGroup *group, - SysprofPage *page) -{ - SysprofDisplay *display; - - g_assert (SYSPROF_IS_VISUALIZER_GROUP (group)); - g_assert (SYSPROF_IS_PAGE (page)); - - display = SYSPROF_DISPLAY (gtk_widget_get_ancestor (GTK_WIDGET (page), SYSPROF_TYPE_DISPLAY)); - sysprof_display_set_visible_page (display, page); -} - -/** - * sysprof_marks_aid_new: - * - * Create a new #SysprofMarksAid. - * - * Returns: (transfer full): a newly created #SysprofMarksAid - */ -SysprofAid * -sysprof_marks_aid_new (void) -{ - return g_object_new (SYSPROF_TYPE_MARKS_AID, NULL); -} - -static bool -find_marks_cb (const SysprofCaptureFrame *frame, - gpointer user_data) -{ - Present *p = user_data; - - g_assert (frame != NULL); - g_assert (p != NULL); - - if (frame->type == SYSPROF_CAPTURE_FRAME_MARK) - { - const SysprofCaptureMark *mark = (const SysprofCaptureMark *)frame; - SysprofMarkTimeSpan span = { frame->time, frame->time + mark->duration }; - gchar joined[64]; - gpointer kptr; - GArray *items; - - p->has_marks = TRUE; - - if G_UNLIKELY (!(items = g_hash_table_lookup (p->categories, mark->group))) - { - items = g_array_new (FALSE, FALSE, sizeof (SysprofMarkTimeSpan)); - g_hash_table_insert (p->categories, g_strdup (mark->group), items); - } - - g_snprintf (joined, sizeof joined, "%s:%s", mark->group, mark->name); - - if G_UNLIKELY (!(kptr = g_hash_table_lookup (p->kinds, joined))) - { - p->last_kind++; - kptr = GINT_TO_POINTER (p->last_kind); - g_hash_table_insert (p->kinds, g_strdup (joined), kptr); - } - - span.kind = GPOINTER_TO_INT (kptr); - - g_array_append_val (items, span); - } - - return TRUE; -} - -static gint -compare_span (const SysprofMarkTimeSpan *a, - const SysprofMarkTimeSpan *b) -{ - if (a->kind < b->kind) - return -1; - - if (b->kind < a->kind) - return 1; - - if (a->begin < b->begin) - return -1; - - if (b->begin < a->begin) - return 1; - - if (b->end > a->end) - return -1; - - return 0; -} - -static void -sysprof_marks_aid_present_worker (GTask *task, - gpointer source_object, - gpointer task_data, - GCancellable *cancellable) -{ - Present *p = task_data; - GHashTableIter iter; - gpointer k, v; - - g_assert (G_IS_TASK (task)); - g_assert (p != NULL); - g_assert (SYSPROF_IS_DISPLAY (p->display)); - g_assert (p->cursor != NULL); - g_assert (SYSPROF_IS_MARKS_AID (source_object)); - - sysprof_capture_cursor_foreach (p->cursor, find_marks_cb, p); - - g_hash_table_iter_init (&iter, p->categories); - while (g_hash_table_iter_next (&iter, &k, &v)) - { - GArray *spans = v; - - g_array_sort (spans, (GCompareFunc)compare_span); - } - - g_task_return_boolean (task, TRUE); -} - -static void -sysprof_marks_aid_present_async (SysprofAid *aid, - SysprofCaptureReader *reader, - SysprofDisplay *display, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - static const SysprofCaptureFrameType marks[] = { - SYSPROF_CAPTURE_FRAME_MARK, - }; - SysprofMarksAid *self = (SysprofMarksAid *)aid; - g_autoptr(GTask) task = NULL; - Present p = {0}; - - g_assert (SYSPROF_IS_MARKS_AID (self)); - - p.display = g_object_ref (display); - p.categories = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, - (GDestroyNotify) g_array_unref); - p.kinds = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - p.cursor = sysprof_capture_cursor_new (reader); - sysprof_capture_cursor_add_condition (p.cursor, - sysprof_capture_condition_new_where_type_in (1, marks)); - - task = g_task_new (self, cancellable, callback, user_data); - g_task_set_source_tag (task, sysprof_marks_aid_present_async); - g_task_set_task_data (task, - g_slice_dup (Present, &p), - present_free); - g_task_run_in_thread (task, sysprof_marks_aid_present_worker); -} - -static gboolean -sysprof_marks_aid_present_finish (SysprofAid *aid, - GAsyncResult *result, - GError **error) -{ - Present *p; - - g_assert (SYSPROF_IS_MARKS_AID (aid)); - g_assert (G_IS_TASK (result)); - - p = g_task_get_task_data (G_TASK (result)); - - if (p->has_marks) - { - g_autoptr(SysprofColorCycle) cycle = sysprof_color_cycle_new (); - SysprofVisualizerGroup *group; - SysprofVisualizer *marks; - SysprofPage *page; - GHashTableIter iter; - gpointer k, v; - - group = g_object_new (SYSPROF_TYPE_VISUALIZER_GROUP, - "can-focus", TRUE, - "has-page", TRUE, - "title", _("Timings"), - "visible", TRUE, - NULL); - - marks = sysprof_mark_visualizer_new (p->categories); - sysprof_visualizer_set_title (marks, _("Timings")); - gtk_widget_show (GTK_WIDGET (marks)); - - g_hash_table_iter_init (&iter, p->categories); - while (g_hash_table_iter_next (&iter, &k, &v)) - { - g_autoptr(GHashTable) seen = g_hash_table_new_full (NULL, NULL, NULL, g_free); - g_autoptr(GHashTable) scoped = NULL; - SysprofVisualizer *scoped_vis; - GArray *spans = v; - const gchar *name = k; - GdkRGBA rgba; - GdkRGBA kind_rgba; - gdouble ratio; - - sysprof_color_cycle_next (cycle, &rgba); - sysprof_mark_visualizer_set_group_rgba (SYSPROF_MARK_VISUALIZER (marks), name, &rgba); - - /* Now make a scoped row just for this group */ - scoped = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, - (GDestroyNotify)g_array_unref); - g_hash_table_insert (scoped, g_strdup (name), g_array_ref (spans)); - - scoped_vis = sysprof_mark_visualizer_new (scoped); - sysprof_visualizer_set_title (scoped_vis, name); - sysprof_mark_visualizer_set_group_rgba (SYSPROF_MARK_VISUALIZER (scoped_vis), name, &rgba); - sysprof_visualizer_group_insert (group, scoped_vis, -1, TRUE); - - ratio = .4 / p->last_kind; - - for (guint i = 0; i < spans->len; i++) - { - const SysprofMarkTimeSpan *span = &g_array_index (spans, SysprofMarkTimeSpan, i); - - if (!g_hash_table_contains (seen, GUINT_TO_POINTER (span->kind))) - { - rgba_shade (&rgba, &kind_rgba, 1 + (ratio * span->kind)); - g_hash_table_insert (seen, - GUINT_TO_POINTER (span->kind), - g_memdup2 (&kind_rgba, sizeof kind_rgba)); - } - } - - sysprof_mark_visualizer_set_kind_rgba (SYSPROF_MARK_VISUALIZER (scoped_vis), seen); - } - - page = g_object_new (SYSPROF_TYPE_MARKS_PAGE, - "zoom-manager", sysprof_display_get_zoom_manager (p->display), - "visible", TRUE, - NULL); - - g_signal_connect_object (group, - "group-activated", - G_CALLBACK (on_group_activated_cb), - page, - 0); - - sysprof_visualizer_group_insert (group, marks, 0, FALSE); - sysprof_display_add_group (p->display, group); - sysprof_display_add_page (p->display, page); - } - - return g_task_propagate_boolean (G_TASK (result), error); -} - -static void -sysprof_marks_aid_class_init (SysprofMarksAidClass *klass) -{ - SysprofAidClass *aid_class = SYSPROF_AID_CLASS (klass); - - aid_class->present_async = sysprof_marks_aid_present_async; - aid_class->present_finish = sysprof_marks_aid_present_finish; -} - -static void -sysprof_marks_aid_init (SysprofMarksAid *self) -{ -} diff --git a/src/libsysprof-ui/sysprof-marks-aid.h b/src/libsysprof-ui/sysprof-marks-aid.h deleted file mode 100644 index f201f3aa..00000000 --- a/src/libsysprof-ui/sysprof-marks-aid.h +++ /dev/null @@ -1,33 +0,0 @@ -/* sysprof-marks-aid.h - * - * Copyright 2019 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-aid.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_MARKS_AID (sysprof_marks_aid_get_type()) - -G_DECLARE_FINAL_TYPE (SysprofMarksAid, sysprof_marks_aid, SYSPROF, MARKS_AID, SysprofAid) - -SysprofAid *sysprof_marks_aid_new (void); - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-marks-model.c b/src/libsysprof-ui/sysprof-marks-model.c deleted file mode 100644 index 08000641..00000000 --- a/src/libsysprof-ui/sysprof-marks-model.c +++ /dev/null @@ -1,592 +0,0 @@ -/* sysprof-marks-model.c - * - * Copyright 2019 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 - */ - -#define G_LOG_DOMAIN "sysprof-marks-model" - -#include "config.h" - -#include - -#include "sysprof-marks-model.h" - -struct _SysprofMarksModel -{ - GObject parent_instance; - GStringChunk *chunks; - GHashTable *counters; - GArray *items; - gint64 max_end_time; -}; - -typedef struct -{ - gint64 begin_time; - gint64 end_time; - const gchar *group; - const gchar *name; - const gchar *message; - SysprofCaptureCounterValue value; - guint is_counter : 1; - guint counter_type : 8; -} Item; - -static void -counter_free (gpointer data) -{ - g_slice_free (SysprofCaptureCounter, data); -} - -static gint -sysprof_marks_model_get_n_columns (GtkTreeModel *model) -{ - return SYSPROF_MARKS_MODEL_COLUMN_LAST; -} - -static GType -sysprof_marks_model_get_column_type (GtkTreeModel *model, - gint column) -{ - switch (column) - { - case SYSPROF_MARKS_MODEL_COLUMN_GROUP: - return G_TYPE_STRING; - - case SYSPROF_MARKS_MODEL_COLUMN_NAME: - return G_TYPE_STRING; - - case SYSPROF_MARKS_MODEL_COLUMN_BEGIN_TIME: - return G_TYPE_INT64; - - case SYSPROF_MARKS_MODEL_COLUMN_END_TIME: - return G_TYPE_INT64; - - case SYSPROF_MARKS_MODEL_COLUMN_DURATION: - return G_TYPE_DOUBLE; - - case SYSPROF_MARKS_MODEL_COLUMN_TEXT: - return G_TYPE_STRING; - - default: - return 0; - } -} - -static GtkTreePath * -sysprof_marks_model_get_path (GtkTreeModel *model, - GtkTreeIter *iter) -{ - gint off; - - g_assert (SYSPROF_IS_MARKS_MODEL (model)); - g_assert (iter != NULL); - - off = GPOINTER_TO_INT (iter->user_data); - - return gtk_tree_path_new_from_indices (off, -1); -} - -static gboolean -sysprof_marks_model_get_iter (GtkTreeModel *model, - GtkTreeIter *iter, - GtkTreePath *path) -{ - SysprofMarksModel *self = (SysprofMarksModel *)model; - gint off; - - g_assert (SYSPROF_IS_MARKS_MODEL (self)); - g_assert (iter != NULL); - g_assert (path != NULL); - - memset (iter, 0, sizeof *iter); - - if (gtk_tree_path_get_depth (path) != 1) - return FALSE; - - off = gtk_tree_path_get_indices (path)[0]; - iter->user_data = GINT_TO_POINTER (off); - - return off >= 0 && off < self->items->len; -} - -static gboolean -sysprof_marks_model_iter_next (GtkTreeModel *model, - GtkTreeIter *iter) -{ - SysprofMarksModel *self = (SysprofMarksModel *)model; - gint off; - - g_assert (SYSPROF_IS_MARKS_MODEL (self)); - g_assert (iter != NULL); - - off = GPOINTER_TO_INT (iter->user_data); - off++; - iter->user_data = GINT_TO_POINTER (off); - - return off < self->items->len; -} - -static gboolean -sysprof_marks_model_iter_nth_child (GtkTreeModel *model, - GtkTreeIter *iter, - GtkTreeIter *parent, - gint n) -{ - SysprofMarksModel *self = (SysprofMarksModel *)model; - - g_assert (SYSPROF_IS_MARKS_MODEL (self)); - g_assert (iter != NULL); - - if (parent != NULL) - return FALSE; - - iter->user_data = GINT_TO_POINTER (n); - - return n < self->items->len; -} - -static gint -sysprof_marks_model_iter_n_children (GtkTreeModel *model, - GtkTreeIter *iter) -{ - SysprofMarksModel *self = (SysprofMarksModel *)model; - - g_assert (SYSPROF_IS_MARKS_MODEL (self)); - - return iter ? 0 : self->items->len; -} - -static gboolean -sysprof_marks_model_iter_has_child (GtkTreeModel *model, - GtkTreeIter *iter) -{ - return FALSE; -} - -static GtkTreeModelFlags -sysprof_marks_model_get_flags (GtkTreeModel *model) -{ - return GTK_TREE_MODEL_LIST_ONLY; -} - -static void -sysprof_marks_model_get_value (GtkTreeModel *model, - GtkTreeIter *iter, - gint column, - GValue *value) -{ - SysprofMarksModel *self = (SysprofMarksModel *)model; - const Item *item; - - g_assert (SYSPROF_IS_MARKS_MODEL (self)); - g_assert (iter != NULL); - g_assert (column < SYSPROF_MARKS_MODEL_COLUMN_LAST); - - item = &g_array_index (self->items, Item, GPOINTER_TO_INT (iter->user_data)); - - switch (column) - { - case SYSPROF_MARKS_MODEL_COLUMN_GROUP: - g_value_init (value, G_TYPE_STRING); - g_value_set_string (value, item->group); - break; - - case SYSPROF_MARKS_MODEL_COLUMN_NAME: - g_value_init (value, G_TYPE_STRING); - g_value_set_string (value, item->name); - break; - - case SYSPROF_MARKS_MODEL_COLUMN_BEGIN_TIME: - g_value_init (value, G_TYPE_INT64); - g_value_set_int64 (value, item->begin_time); - break; - - case SYSPROF_MARKS_MODEL_COLUMN_END_TIME: - g_value_init (value, G_TYPE_INT64); - g_value_set_int64 (value, item->end_time); - break; - - case SYSPROF_MARKS_MODEL_COLUMN_DURATION: - g_value_init (value, G_TYPE_DOUBLE); - if (item->end_time) - g_value_set_double (value, (item->end_time - item->begin_time) / (double)(G_USEC_PER_SEC * 1000)); - break; - - case SYSPROF_MARKS_MODEL_COLUMN_TEXT: - g_value_init (value, G_TYPE_STRING); - if (item->is_counter) - { - gchar *val = NULL; - - if (item->counter_type == SYSPROF_CAPTURE_COUNTER_DOUBLE) - val = g_strdup_printf ("%s — %s = %.4lf", item->group, item->name, item->value.vdbl); - else if (item->counter_type == SYSPROF_CAPTURE_COUNTER_INT64) - val = g_strdup_printf ("%s — %s = %"G_GINT64_FORMAT, item->group, item->name, item->value.v64); - - g_value_take_string (value, g_steal_pointer (&val)); - } - else - { - if (item->message && item->message[0]) - g_value_take_string (value, g_strdup_printf ("%s — %s", item->name, item->message)); - else - g_value_set_string (value, item->name); - } - break; - - default: - break; - } -} - -static void -tree_model_iface_init (GtkTreeModelIface *iface) -{ - iface->get_n_columns = sysprof_marks_model_get_n_columns; - iface->get_column_type = sysprof_marks_model_get_column_type; - iface->get_iter = sysprof_marks_model_get_iter; - iface->get_path = sysprof_marks_model_get_path; - iface->iter_next = sysprof_marks_model_iter_next; - iface->iter_n_children = sysprof_marks_model_iter_n_children; - iface->iter_nth_child = sysprof_marks_model_iter_nth_child; - iface->iter_has_child = sysprof_marks_model_iter_has_child; - iface->get_flags = sysprof_marks_model_get_flags; - iface->get_value = sysprof_marks_model_get_value; -} - -G_DEFINE_TYPE_WITH_CODE (SysprofMarksModel, sysprof_marks_model, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL, tree_model_iface_init)) - -static void -sysprof_marks_model_finalize (GObject *object) -{ - SysprofMarksModel *self = (SysprofMarksModel *)object; - - g_clear_pointer (&self->counters, g_hash_table_unref); - g_clear_pointer (&self->items, g_array_unref); - g_clear_pointer (&self->chunks, g_string_chunk_free); - - G_OBJECT_CLASS (sysprof_marks_model_parent_class)->finalize (object); -} - -static void -sysprof_marks_model_class_init (SysprofMarksModelClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->finalize = sysprof_marks_model_finalize; -} - -static void -sysprof_marks_model_init (SysprofMarksModel *self) -{ - self->counters = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, counter_free); - self->chunks = g_string_chunk_new (4096*16); - self->items = g_array_new (FALSE, FALSE, sizeof (Item)); -} - -static bool -cursor_foreach_cb (const SysprofCaptureFrame *frame, - gpointer user_data) -{ - SysprofMarksModel *self = user_data; - Item item; - - g_assert (SYSPROF_IS_MARKS_MODEL (self)); - g_assert (frame->type == SYSPROF_CAPTURE_FRAME_MARK || - frame->type == SYSPROF_CAPTURE_FRAME_CTRSET || - frame->type == SYSPROF_CAPTURE_FRAME_CTRDEF || - frame->type == SYSPROF_CAPTURE_FRAME_FORK); - - if (frame->type == SYSPROF_CAPTURE_FRAME_MARK) - { - SysprofCaptureMark *mark = (SysprofCaptureMark *)frame; - - item.begin_time = frame->time; - item.end_time = item.begin_time + mark->duration; - item.group = g_string_chunk_insert_const (self->chunks, mark->group); - item.name = g_string_chunk_insert_const (self->chunks, mark->name); - item.message = g_string_chunk_insert_const (self->chunks, mark->message); - item.value.v64 = 0; - item.is_counter = FALSE; - item.counter_type = 0; - - if G_LIKELY (item.end_time > self->max_end_time) - self->max_end_time = item.end_time; - - g_array_append_val (self->items, item); - } - else if (frame->type == SYSPROF_CAPTURE_FRAME_FORK) - { - SysprofCaptureFork *fk = (SysprofCaptureFork *)frame; - g_autofree gchar *message = g_strdup_printf ("PID: %d, Child PID: %d", frame->pid, fk->child_pid); - - item.begin_time = frame->time; - item.end_time = item.begin_time; - item.group = g_string_chunk_insert_const (self->chunks, "fork"); - item.name = g_string_chunk_insert_const (self->chunks, "Fork"); - item.message = g_string_chunk_insert_const (self->chunks, message); - item.value.v64 = 0; - item.is_counter = FALSE; - item.counter_type = 0; - - g_array_append_val (self->items, item); - } - else if (frame->type == SYSPROF_CAPTURE_FRAME_CTRDEF) - { - SysprofCaptureCounterDefine *ctrdef = (SysprofCaptureCounterDefine *)frame; - - for (guint i = 0; i < ctrdef->n_counters; i++) - { - SysprofCaptureCounter *ctr = &ctrdef->counters[i]; - - g_hash_table_insert (self->counters, - GUINT_TO_POINTER ((guint)ctr->id), - g_slice_dup (SysprofCaptureCounter, ctr)); - } - } - else if (frame->type == SYSPROF_CAPTURE_FRAME_CTRSET) - { - SysprofCaptureCounterSet *ctrset = (SysprofCaptureCounterSet *)frame; - - for (guint i = 0; i < ctrset->n_values; i++) - { - SysprofCaptureCounterValues *values = &ctrset->values[i]; - - for (guint j = 0; j < G_N_ELEMENTS (values->ids); j++) - { - guint32 id = values->ids[j]; - SysprofCaptureCounter *ctr = NULL; - - if (id == 0) - break; - - if ((ctr = g_hash_table_lookup (self->counters, GUINT_TO_POINTER (id)))) - { - item.begin_time = frame->time; - item.end_time = frame->time; - item.group = ctr->category; - item.name = ctr->name; - item.message = NULL; - item.is_counter = TRUE; - item.counter_type = ctr->type; - - memcpy (&item.value, &values->values[j], sizeof item.value); - - g_array_append_val (self->items, item); - } - } - - } - } - - return TRUE; -} - -static gint -item_compare (gconstpointer a, - gconstpointer b) -{ - const Item *ia = a; - const Item *ib = b; - - if (ia->begin_time < ib->begin_time) - return -1; - else if (ia->begin_time > ib->begin_time) - return 1; - - /* Sort items with longer duration first, as they might be - * "overarching" marks containing other marks. - */ - if (ia->end_time > ib->end_time) - return -1; - else if (ib->end_time > ia->end_time) - return 1; - - return 0; -} - -static void -sysprof_marks_model_new_worker (GTask *task, - gpointer source_object, - gpointer task_data, - GCancellable *cancellable) -{ - g_autoptr(SysprofMarksModel) self = NULL; - SysprofCaptureCursor *cursor = task_data; - - g_assert (G_IS_TASK (task)); - g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); - - self = g_object_new (SYSPROF_TYPE_MARKS_MODEL, NULL); - sysprof_capture_cursor_foreach (cursor, cursor_foreach_cb, self); - g_array_sort (self->items, item_compare); - - g_task_return_pointer (task, g_steal_pointer (&self), g_object_unref); -} - -static void -sysprof_marks_model_selection_foreach_cb (SysprofSelection *selection, - gint64 begin, - gint64 end, - gpointer user_data) -{ - SysprofCaptureCondition **condition = user_data; - SysprofCaptureCondition *c; - - g_assert (SYSPROF_IS_SELECTION (selection)); - g_assert (condition != NULL); - - c = sysprof_capture_condition_new_where_time_between (begin, end); - - if (*condition != NULL) - *condition = sysprof_capture_condition_new_or (g_steal_pointer (&c), - g_steal_pointer (condition)); - else - *condition = g_steal_pointer (&c); -} - -void -sysprof_marks_model_new_async (SysprofCaptureReader *reader, - SysprofMarksModelKind kind, - SysprofSelection *selection, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - static const SysprofCaptureFrameType ctrset[] = { SYSPROF_CAPTURE_FRAME_CTRDEF }; - g_autoptr(SysprofCaptureCursor) cursor = NULL; - g_autoptr(GTask) task = NULL; - SysprofCaptureCondition *c; - - g_return_if_fail (reader != NULL); - g_return_if_fail (!selection || SYSPROF_IS_SELECTION (selection)); - g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); - - cursor = sysprof_capture_cursor_new (reader); - - if (kind == SYSPROF_MARKS_MODEL_BOTH) - { - static const SysprofCaptureFrameType types[] = { - SYSPROF_CAPTURE_FRAME_CTRSET, - SYSPROF_CAPTURE_FRAME_MARK, - }; - - c = sysprof_capture_condition_new_where_type_in (G_N_ELEMENTS (types), types); - } - else if (kind == SYSPROF_MARKS_MODEL_MARKS) - { - static const SysprofCaptureFrameType types[] = { - SYSPROF_CAPTURE_FRAME_MARK, - SYSPROF_CAPTURE_FRAME_FORK, - }; - - c = sysprof_capture_condition_new_where_type_in (G_N_ELEMENTS (types), types); - } - else if (kind == SYSPROF_MARKS_MODEL_COUNTERS) - { - static const SysprofCaptureFrameType types[] = { SYSPROF_CAPTURE_FRAME_CTRSET }; - - c = sysprof_capture_condition_new_where_type_in (G_N_ELEMENTS (types), types); - } - else - { - g_task_report_new_error (NULL, callback, user_data, - sysprof_marks_model_new_async, - G_IO_ERROR, - G_IO_ERROR_INVAL, - "Invalid arguments"); - return; - } - - if (selection) - { - SysprofCaptureCondition *condition = NULL; - - sysprof_selection_foreach (selection, - sysprof_marks_model_selection_foreach_cb, - &condition); - if (condition) - c = sysprof_capture_condition_new_and (c, g_steal_pointer (&condition)); - } - - if (kind & SYSPROF_MARKS_MODEL_COUNTERS) - { - c = sysprof_capture_condition_new_or ( - sysprof_capture_condition_new_where_type_in (G_N_ELEMENTS (ctrset), ctrset), - g_steal_pointer (&c)); - } - - sysprof_capture_cursor_add_condition (cursor, g_steal_pointer (&c)); - - task = g_task_new (NULL, cancellable, callback, user_data); - g_task_set_source_tag (task, sysprof_marks_model_new_async); - g_task_set_task_data (task, - g_steal_pointer (&cursor), - (GDestroyNotify) sysprof_capture_cursor_unref); - g_task_run_in_thread (task, sysprof_marks_model_new_worker); -} - -SysprofMarksModel * -sysprof_marks_model_new_finish (GAsyncResult *result, - GError **error) -{ - g_return_val_if_fail (G_IS_TASK (result), NULL); - - return g_task_propagate_pointer (G_TASK (result), error); -} - -void -sysprof_marks_model_get_range (SysprofMarksModel *self, - gint64 *begin_time, - gint64 *end_time) -{ - g_return_if_fail (SYSPROF_IS_MARKS_MODEL (self)); - - if (begin_time != NULL) - { - *begin_time = 0; - - if (self->items->len > 0) - *begin_time = g_array_index (self->items, Item, 0).begin_time; - } - - if (end_time != NULL) - *end_time = self->max_end_time; -} - -GType -sysprof_marks_model_kind_get_type (void) -{ - static GType type_id; - - if (g_once_init_enter (&type_id)) - { - static const GEnumValue values[] = { - { SYSPROF_MARKS_MODEL_MARKS, "SYSPROF_MARKS_MODEL_MARKS", "marks" }, - { SYSPROF_MARKS_MODEL_COUNTERS, "SYSPROF_MARKS_MODEL_COUNTERS", "counters" }, - { SYSPROF_MARKS_MODEL_BOTH, "SYSPROF_MARKS_MODEL_BOTH", "both" }, - { 0 }, - }; - GType _type_id = g_enum_register_static ("SysprofMarksModelKind", values); - g_once_init_leave (&type_id, _type_id); - } - - return type_id; -} diff --git a/src/libsysprof-ui/sysprof-marks-model.h b/src/libsysprof-ui/sysprof-marks-model.h deleted file mode 100644 index 203ad8ec..00000000 --- a/src/libsysprof-ui/sysprof-marks-model.h +++ /dev/null @@ -1,65 +0,0 @@ -/* sysprof-marks-model.h - * - * Copyright 2019 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 - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_MARKS_MODEL (sysprof_marks_model_get_type()) -#define SYSPROF_TYPE_MARKS_MODEL_KIND (sysprof_marks_model_kind_get_type()) - -typedef enum -{ - SYSPROF_MARKS_MODEL_COLUMN_GROUP, - SYSPROF_MARKS_MODEL_COLUMN_NAME, - SYSPROF_MARKS_MODEL_COLUMN_BEGIN_TIME, - SYSPROF_MARKS_MODEL_COLUMN_END_TIME, - SYSPROF_MARKS_MODEL_COLUMN_DURATION, - SYSPROF_MARKS_MODEL_COLUMN_TEXT, -} SysprofMarksModelColumn; - -typedef enum -{ - SYSPROF_MARKS_MODEL_MARKS = 1, - SYSPROF_MARKS_MODEL_COUNTERS, - SYSPROF_MARKS_MODEL_BOTH = SYSPROF_MARKS_MODEL_MARKS | SYSPROF_MARKS_MODEL_COUNTERS, -} SysprofMarksModelKind; - -#define SYSPROF_MARKS_MODEL_COLUMN_LAST (SYSPROF_MARKS_MODEL_COLUMN_TEXT+1) - -G_DECLARE_FINAL_TYPE (SysprofMarksModel, sysprof_marks_model, SYSPROF, MARKS_MODEL, GObject) - -GType sysprof_marks_model_kind_get_type (void) G_GNUC_CONST; -void sysprof_marks_model_new_async (SysprofCaptureReader *reader, - SysprofMarksModelKind kind, - SysprofSelection *selection, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -SysprofMarksModel *sysprof_marks_model_new_finish (GAsyncResult *result, - GError **error); -void sysprof_marks_model_get_range (SysprofMarksModel *self, - gint64 *begin_time, - gint64 *end_time); - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-marks-page.c b/src/libsysprof-ui/sysprof-marks-page.c deleted file mode 100644 index c08381b9..00000000 --- a/src/libsysprof-ui/sysprof-marks-page.c +++ /dev/null @@ -1,610 +0,0 @@ -/* sysprof-marks-page.c - * - * Copyright 2019 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 - */ - -#define G_LOG_DOMAIN "sysprof-marks-page" - -#include "config.h" - -#include "sysprof-cell-renderer-duration.h" -#include "sysprof-marks-model.h" -#include "sysprof-marks-page.h" -#include "sysprof-ui-private.h" -#include "sysprof-zoom-manager.h" - -typedef struct -{ - SysprofMarksModelKind kind; - - SysprofZoomManager *zoom_manager; - - gint64 capture_begin_time; - gint64 capture_end_time; - - /* Template objects */ - GtkScrolledWindow *scroller; - GtkTreeView *tree_view; - GtkBox *details_box; - GtkTreeViewColumn *duration_column; - SysprofCellRendererDuration *duration_cell; - GtkStack *stack; - GtkLabel *group; - GtkLabel *mark; - GtkLabel *time; - GtkLabel *end; - GtkLabel *duration; - GtkTextView *message; - GtkWidget *failed; - GtkWidget *marks; -} SysprofMarksPagePrivate; - -enum { - PROP_0, - PROP_KIND, - PROP_ZOOM_MANAGER, - N_PROPS -}; - -static GParamSpec *properties [N_PROPS]; - -G_DEFINE_TYPE_WITH_PRIVATE (SysprofMarksPage, sysprof_marks_page, SYSPROF_TYPE_PAGE) - -static gboolean -sysprof_marks_page_tree_view_key_press_event_cb (SysprofMarksPage *self, - guint keyval, - guint keycode, - GdkModifierType state, - GtkEventControllerKey *controller) -{ - SysprofMarksPagePrivate *priv = sysprof_marks_page_get_instance_private (self); - gint dir = 0; - - g_assert (SYSPROF_MARKS_PAGE (self)); - g_assert (GTK_IS_EVENT_CONTROLLER_KEY (controller)); - - if ((state & (GDK_SHIFT_MASK|GDK_CONTROL_MASK|GDK_ALT_MASK)) == 0) - { - switch (keyval) - { - case GDK_KEY_Left: - dir = -1; - break; - - case GDK_KEY_Right: - dir = 1; - break; - - default: - break; - } - - if (dir) - { - GtkAdjustment *adj = gtk_scrolled_window_get_hadjustment (priv->scroller); - gdouble step = gtk_adjustment_get_step_increment (adj); - gdouble val = CLAMP (gtk_adjustment_get_value (adj) + (step * dir), - gtk_adjustment_get_lower (adj), - gtk_adjustment_get_upper (adj)); - gtk_adjustment_set_value (adj, val); - return GDK_EVENT_STOP; - } - } - - return GDK_EVENT_PROPAGATE; -} - -static gboolean -get_first_selected (GtkTreeSelection *selection, - GtkTreeModel **model, - GtkTreeIter *iter) -{ - GtkTreeModel *m; - - g_assert (GTK_IS_TREE_SELECTION (selection)); - - if (gtk_tree_selection_count_selected_rows (selection) != 1) - return FALSE; - - m = gtk_tree_view_get_model (gtk_tree_selection_get_tree_view (selection)); - if (model) - *model = m; - - if (iter) - { - GList *paths = gtk_tree_selection_get_selected_rows (selection, model); - gtk_tree_model_get_iter (m, iter, paths->data); - g_list_free_full (paths, (GDestroyNotify)gtk_tree_path_free); - } - - return TRUE; -} - -static void -sysprof_marks_page_selection_changed_cb (SysprofMarksPage *self, - GtkTreeSelection *selection) -{ - SysprofMarksPagePrivate *priv = sysprof_marks_page_get_instance_private (self); - GtkTreeModel *model; - GtkTreeIter iter; - - g_assert (SYSPROF_IS_MARKS_PAGE (self)); - g_assert (GTK_IS_TREE_SELECTION (selection)); - - if (get_first_selected (selection, &model, &iter)) - { - g_autofree gchar *group = NULL; - g_autofree gchar *name = NULL; - g_autofree gchar *duration_str = NULL; - g_autofree gchar *time_str = NULL; - g_autofree gchar *end_str = NULL; - g_autofree gchar *text = NULL; - GtkAdjustment *adj; - gdouble x; - gint64 begin_time; - gint64 end_time; - gint64 duration; - gint64 etime; - gint64 otime; - gdouble lower; - gdouble upper; - gdouble value; - gdouble page_size; - gint width; - - gtk_tree_model_get (model, &iter, - SYSPROF_MARKS_MODEL_COLUMN_GROUP, &group, - SYSPROF_MARKS_MODEL_COLUMN_NAME, &name, - SYSPROF_MARKS_MODEL_COLUMN_BEGIN_TIME, &begin_time, - SYSPROF_MARKS_MODEL_COLUMN_END_TIME, &end_time, - SYSPROF_MARKS_MODEL_COLUMN_TEXT, &text, - -1); - - duration = end_time - begin_time; - duration_str = _sysprof_format_duration (duration); - - otime = begin_time - priv->capture_begin_time; - time_str = _sysprof_format_duration (otime); - - etime = end_time - priv->capture_begin_time; - end_str = _sysprof_format_duration (etime); - - gtk_label_set_label (priv->group, group); - gtk_label_set_label (priv->mark, name); - gtk_label_set_label (priv->duration, duration_str); - gtk_label_set_label (priv->time, time_str); - gtk_label_set_label (priv->end, end_str); - - gtk_text_buffer_set_text (gtk_text_view_get_buffer (priv->message), text, -1); - - adj = gtk_scrolled_window_get_hadjustment (priv->scroller); - width = gtk_tree_view_column_get_width (priv->duration_column); - x = sysprof_zoom_manager_get_offset_at_time (priv->zoom_manager, - begin_time - priv->capture_begin_time, - width); - - g_object_get (adj, - "lower", &lower, - "upper", &upper, - "value", &value, - "page-size", &page_size, - NULL); - - if (x < value) - gtk_adjustment_set_value (adj, MAX (lower, x - (page_size / 3.0))); - else if (x > (value + page_size)) - gtk_adjustment_set_value (adj, MIN (upper - page_size, (x - (page_size / 3.0)))); - } -} - -static gboolean -sysprof_marks_page_tree_view_query_tooltip_cb (SysprofMarksPage *self, - gint x, - gint y, - gboolean keyboard_mode, - GtkTooltip *tooltip, - GtkTreeView *tree_view) -{ - SysprofMarksPagePrivate *priv = sysprof_marks_page_get_instance_private (self); - GtkTreeViewColumn *column; - GtkTreePath *path = NULL; - gint cell_x, cell_y; - gboolean ret = FALSE; - - g_assert (SYSPROF_IS_MARKS_PAGE (self)); - g_assert (GTK_IS_TOOLTIP (tooltip)); - g_assert (GTK_IS_TREE_VIEW (tree_view)); - - if (gtk_tree_view_get_path_at_pos (tree_view, x, y, &path, &column, &cell_x, &cell_y)) - { - GtkTreeModel *model = gtk_tree_view_get_model (tree_view); - GtkTreeIter iter; - - if (gtk_tree_model_get_iter (model, &iter, path)) - { - g_autofree gchar *text = NULL; - g_autofree gchar *timestr = NULL; - g_autofree gchar *tooltip_text = NULL; - g_autofree gchar *durationstr = NULL; - gint64 begin_time; - gint64 end_time; - gint64 duration; - - gtk_tree_model_get (model, &iter, - SYSPROF_MARKS_MODEL_COLUMN_BEGIN_TIME, &begin_time, - SYSPROF_MARKS_MODEL_COLUMN_END_TIME, &end_time, - SYSPROF_MARKS_MODEL_COLUMN_TEXT, &text, - -1); - - duration = end_time - begin_time; - begin_time -= priv->capture_begin_time; - durationstr = _sysprof_format_duration (duration); - - if (duration != 0) - timestr = g_strdup_printf ("%0.4lf (%s)", begin_time / (gdouble)SYSPROF_NSEC_PER_SEC, durationstr); - else - timestr = g_strdup_printf ("%0.4lf", begin_time / (gdouble)SYSPROF_NSEC_PER_SEC); - - tooltip_text = g_strdup_printf ("%s: %s", timestr, text); - - gtk_tooltip_set_text (tooltip, tooltip_text); - - ret = TRUE; - } - } - - gtk_tree_path_free (path); - - return ret; -} - -static void -sysprof_marks_page_load_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - g_autoptr(SysprofMarksModel) model = NULL; - g_autoptr(GError) error = NULL; - g_autoptr(GTask) task = user_data; - SysprofMarksPagePrivate *priv; - SysprofCaptureReader *reader; - SysprofMarksPage *self; - - g_assert (G_IS_ASYNC_RESULT (result)); - g_assert (G_IS_TASK (task)); - - self = g_task_get_source_object (task); - priv = sysprof_marks_page_get_instance_private (self); - - if (!(model = sysprof_marks_model_new_finish (result, &error))) - { - g_task_return_error (task, g_steal_pointer (&error)); - return; - } - - reader = g_task_get_task_data (task); - g_assert (reader != NULL); - - priv->capture_begin_time = sysprof_capture_reader_get_start_time (reader); - priv->capture_end_time = sysprof_capture_reader_get_end_time (reader); - - g_object_set (priv->duration_cell, - "capture-begin-time", priv->capture_begin_time, - "capture-end-time", priv->capture_end_time, - "zoom-manager", priv->zoom_manager, - NULL); - - gtk_tree_view_set_model (priv->tree_view, GTK_TREE_MODEL (model)); - - if (gtk_tree_model_iter_n_children (GTK_TREE_MODEL (model), NULL) == 0) - gtk_stack_set_visible_child (priv->stack, GTK_WIDGET (priv->failed)); - else - gtk_stack_set_visible_child (priv->stack, GTK_WIDGET (priv->marks)); - - g_task_return_boolean (task, TRUE); -} - -static void -sysprof_marks_page_load_async (SysprofPage *page, - SysprofCaptureReader *reader, - SysprofSelection *selection, - SysprofCaptureCondition *filter, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - SysprofMarksPage *self = (SysprofMarksPage *)page; - SysprofMarksPagePrivate *priv = sysprof_marks_page_get_instance_private (self); - g_autoptr(GTask) task = NULL; - - g_return_if_fail (SYSPROF_IS_MARKS_PAGE (self)); - g_return_if_fail (reader != NULL); - g_return_if_fail (!selection || SYSPROF_IS_SELECTION (selection)); - g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); - - task = g_task_new (self, cancellable, callback, user_data); - g_task_set_source_tag (task, sysprof_marks_page_load_async); - g_task_set_task_data (task, - sysprof_capture_reader_ref (reader), - (GDestroyNotify) sysprof_capture_reader_unref); - - sysprof_marks_model_new_async (reader, - priv->kind, - selection, - cancellable, - sysprof_marks_page_load_cb, - g_steal_pointer (&task)); -} - -static gboolean -sysprof_marks_page_load_finish (SysprofPage *page, - GAsyncResult *result, - GError **error) -{ - g_return_val_if_fail (SYSPROF_IS_MARKS_PAGE (page), FALSE); - g_return_val_if_fail (G_IS_TASK (result), FALSE); - - return g_task_propagate_boolean (G_TASK (result), error); -} - -static void -sysprof_marks_page_set_hadjustment (SysprofPage *page, - GtkAdjustment *hadjustment) -{ - SysprofMarksPage *self = (SysprofMarksPage *)page; - SysprofMarksPagePrivate *priv = sysprof_marks_page_get_instance_private (self); - - g_assert (SYSPROF_IS_MARKS_PAGE (self)); - g_assert (!hadjustment || GTK_IS_ADJUSTMENT (hadjustment)); - - gtk_scrolled_window_set_hadjustment (priv->scroller, hadjustment); -} - -static void -sysprof_marks_page_set_size_group (SysprofPage *page, - GtkSizeGroup *size_group) -{ - SysprofMarksPage *self = (SysprofMarksPage *)page; - SysprofMarksPagePrivate *priv = sysprof_marks_page_get_instance_private (self); - - g_assert (SYSPROF_IS_MARKS_PAGE (self)); - g_assert (GTK_IS_SIZE_GROUP (size_group)); - - gtk_size_group_add_widget (size_group, GTK_WIDGET (priv->details_box)); -} - -static void -sysprof_marks_page_tree_view_row_activated_cb (SysprofMarksPage *self, - GtkTreePath *path, - GtkTreeViewColumn *column, - GtkTreeView *tree_view) -{ - GtkTreeModel *model; - GtkTreeIter iter; - - g_assert (SYSPROF_IS_MARKS_PAGE (self)); - g_assert (path != NULL); - g_assert (GTK_IS_TREE_VIEW_COLUMN (column)); - g_assert (GTK_IS_TREE_VIEW (tree_view)); - - model = gtk_tree_view_get_model (tree_view); - - if (gtk_tree_model_get_iter (model, &iter, path)) - { - SysprofDisplay *display; - gint64 begin_time; - gint64 end_time; - - gtk_tree_model_get (model, &iter, - SYSPROF_MARKS_MODEL_COLUMN_BEGIN_TIME, &begin_time, - SYSPROF_MARKS_MODEL_COLUMN_END_TIME, &end_time, - -1); - - display = SYSPROF_DISPLAY (gtk_widget_get_ancestor (GTK_WIDGET (self), SYSPROF_TYPE_DISPLAY)); - sysprof_display_add_to_selection (display, begin_time, end_time); - } -} - -static void -sysprof_marks_page_finalize (GObject *object) -{ - SysprofMarksPage *self = (SysprofMarksPage *)object; - SysprofMarksPagePrivate *priv = sysprof_marks_page_get_instance_private (self); - - g_clear_object (&priv->zoom_manager); - - G_OBJECT_CLASS (sysprof_marks_page_parent_class)->finalize (object); -} - -static void -sysprof_marks_page_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - SysprofMarksPage *self = SYSPROF_MARKS_PAGE (object); - SysprofMarksPagePrivate *priv = sysprof_marks_page_get_instance_private (self); - - switch (prop_id) - { - case PROP_KIND: - g_value_set_enum (value, priv->kind); - break; - - case PROP_ZOOM_MANAGER: - g_value_set_object (value, priv->zoom_manager); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_marks_page_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - SysprofMarksPage *self = SYSPROF_MARKS_PAGE (object); - SysprofMarksPagePrivate *priv = sysprof_marks_page_get_instance_private (self); - - switch (prop_id) - { - case PROP_KIND: - priv->kind = g_value_get_enum (value); - break; - - case PROP_ZOOM_MANAGER: - if (g_set_object (&priv->zoom_manager, g_value_get_object (value))) - { - g_object_set (priv->duration_cell, - "zoom-manager", priv->zoom_manager, - NULL); - if (priv->zoom_manager) - g_signal_connect_object (priv->zoom_manager, - "notify::zoom", - G_CALLBACK (gtk_tree_view_column_queue_resize), - priv->duration_column, - G_CONNECT_SWAPPED); - } - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_marks_page_class_init (SysprofMarksPageClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); - SysprofPageClass *page_class = SYSPROF_PAGE_CLASS (klass); - - object_class->finalize = sysprof_marks_page_finalize; - object_class->get_property = sysprof_marks_page_get_property; - object_class->set_property = sysprof_marks_page_set_property; - - page_class->load_async = sysprof_marks_page_load_async; - page_class->load_finish = sysprof_marks_page_load_finish; - page_class->set_hadjustment = sysprof_marks_page_set_hadjustment; - page_class->set_size_group = sysprof_marks_page_set_size_group; - - gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/sysprof/ui/sysprof-marks-page.ui"); - gtk_widget_class_bind_template_child_private (widget_class, SysprofMarksPage, end); - gtk_widget_class_bind_template_child_private (widget_class, SysprofMarksPage, details_box); - gtk_widget_class_bind_template_child_private (widget_class, SysprofMarksPage, duration_cell); - gtk_widget_class_bind_template_child_private (widget_class, SysprofMarksPage, duration_column); - gtk_widget_class_bind_template_child_private (widget_class, SysprofMarksPage, scroller); - gtk_widget_class_bind_template_child_private (widget_class, SysprofMarksPage, stack); - gtk_widget_class_bind_template_child_private (widget_class, SysprofMarksPage, tree_view); - gtk_widget_class_bind_template_child_private (widget_class, SysprofMarksPage, group); - gtk_widget_class_bind_template_child_private (widget_class, SysprofMarksPage, mark); - gtk_widget_class_bind_template_child_private (widget_class, SysprofMarksPage, duration); - gtk_widget_class_bind_template_child_private (widget_class, SysprofMarksPage, time); - gtk_widget_class_bind_template_child_private (widget_class, SysprofMarksPage, message); - gtk_widget_class_bind_template_child_private (widget_class, SysprofMarksPage, marks); - gtk_widget_class_bind_template_child_private (widget_class, SysprofMarksPage, failed); - - properties [PROP_KIND] = - g_param_spec_enum ("kind", NULL, NULL, - SYSPROF_TYPE_MARKS_MODEL_KIND, - SYSPROF_MARKS_MODEL_MARKS, - (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); - - properties [PROP_ZOOM_MANAGER] = - g_param_spec_object ("zoom-manager", NULL, NULL, - SYSPROF_TYPE_ZOOM_MANAGER, - (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_properties (object_class, N_PROPS, properties); - - g_type_ensure (SYSPROF_TYPE_CELL_RENDERER_DURATION); -} - -static void -sysprof_marks_page_init (SysprofMarksPage *self) -{ - SysprofMarksPagePrivate *priv = sysprof_marks_page_get_instance_private (self); - GtkEventController *controller; - - priv->kind = SYSPROF_MARKS_MODEL_MARKS; - - gtk_widget_init_template (GTK_WIDGET (self)); - - gtk_tree_selection_set_mode (gtk_tree_view_get_selection (priv->tree_view), - GTK_SELECTION_MULTIPLE); - - controller = gtk_event_controller_key_new (); - gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_CAPTURE); - g_signal_connect_object (controller, - "key-pressed", - G_CALLBACK (sysprof_marks_page_tree_view_key_press_event_cb), - self, - G_CONNECT_SWAPPED); - gtk_widget_add_controller (GTK_WIDGET (self), controller); - - g_signal_connect_object (priv->tree_view, - "row-activated", - G_CALLBACK (sysprof_marks_page_tree_view_row_activated_cb), - self, - G_CONNECT_SWAPPED); - - g_signal_connect_object (priv->tree_view, - "query-tooltip", - G_CALLBACK (sysprof_marks_page_tree_view_query_tooltip_cb), - self, - G_CONNECT_SWAPPED); - - g_signal_connect_object (gtk_tree_view_get_selection (priv->tree_view), - "changed", - G_CALLBACK (sysprof_marks_page_selection_changed_cb), - self, - G_CONNECT_SWAPPED); -} - -GtkWidget * -sysprof_marks_page_new (SysprofZoomManager *zoom_manager, - SysprofMarksModelKind kind) -{ - SysprofMarksPage *self; - SysprofMarksPagePrivate *priv; - - g_return_val_if_fail (SYSPROF_IS_ZOOM_MANAGER (zoom_manager), NULL); - - self = g_object_new (SYSPROF_TYPE_MARKS_PAGE, - "zoom-manager", zoom_manager, - NULL); - priv = sysprof_marks_page_get_instance_private (self); - priv->kind = kind; - - return GTK_WIDGET (self); -} - -void -_sysprof_marks_page_set_hadjustment (SysprofMarksPage *self, - GtkAdjustment *hadjustment) -{ - SysprofMarksPagePrivate *priv = sysprof_marks_page_get_instance_private (self); - - g_return_if_fail (SYSPROF_IS_MARKS_PAGE (self)); - g_return_if_fail (GTK_IS_ADJUSTMENT (hadjustment)); - - gtk_scrolled_window_set_hadjustment (priv->scroller, hadjustment); -} diff --git a/src/libsysprof-ui/sysprof-marks-page.h b/src/libsysprof-ui/sysprof-marks-page.h deleted file mode 100644 index 5070b1ae..00000000 --- a/src/libsysprof-ui/sysprof-marks-page.h +++ /dev/null @@ -1,44 +0,0 @@ -/* sysprof-marks-page.h - * - * Copyright 2019 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-marks-model.h" -#include "sysprof-page.h" -#include "sysprof-zoom-manager.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_MARKS_PAGE (sysprof_marks_page_get_type()) - -G_DECLARE_DERIVABLE_TYPE (SysprofMarksPage, sysprof_marks_page, SYSPROF, MARKS_PAGE, SysprofPage) - -struct _SysprofMarksPageClass -{ - SysprofPageClass parent_class; - - /*< private >*/ - gpointer _reserved[16]; -}; - -GtkWidget *sysprof_marks_page_new (SysprofZoomManager *zoom_manager, - SysprofMarksModelKind kind); - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-marks-page.ui b/src/libsysprof-ui/sysprof-marks-page.ui deleted file mode 100644 index 49936079..00000000 --- a/src/libsysprof-ui/sysprof-marks-page.ui +++ /dev/null @@ -1,259 +0,0 @@ - - - - diff --git a/src/libsysprof-ui/sysprof-memory-aid.c b/src/libsysprof-ui/sysprof-memory-aid.c deleted file mode 100644 index 67341ace..00000000 --- a/src/libsysprof-ui/sysprof-memory-aid.c +++ /dev/null @@ -1,70 +0,0 @@ -/* sysprof-memory-aid.c - * - * Copyright 2019 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 - */ - -#define G_LOG_DOMAIN "sysprof-memory-aid" - -#include "config.h" - -#include - -#include "sysprof-memory-aid.h" - -struct _SysprofMemoryAid -{ - SysprofAid parent_instance; -}; - -G_DEFINE_TYPE (SysprofMemoryAid, sysprof_memory_aid, SYSPROF_TYPE_AID) - -SysprofAid * -sysprof_memory_aid_new (void) -{ - return g_object_new (SYSPROF_TYPE_MEMORY_AID, NULL); -} - -static void -sysprof_memory_aid_prepare (SysprofAid *self, - SysprofProfiler *profiler) -{ -#ifdef __linux__ - g_autoptr(SysprofSource) source = NULL; - - g_assert (SYSPROF_IS_MEMORY_AID (self)); - g_assert (SYSPROF_IS_PROFILER (profiler)); - - source = sysprof_memory_source_new (); - sysprof_profiler_add_source (profiler, source); -#endif -} - -static void -sysprof_memory_aid_class_init (SysprofMemoryAidClass *klass) -{ - SysprofAidClass *aid_class = SYSPROF_AID_CLASS (klass); - - aid_class->prepare = sysprof_memory_aid_prepare; -} - -static void -sysprof_memory_aid_init (SysprofMemoryAid *self) -{ - sysprof_aid_set_display_name (SYSPROF_AID (self), _("Memory Usage")); - sysprof_aid_set_icon_name (SYSPROF_AID (self), "org.gnome.Sysprof-symbolic"); -} diff --git a/src/libsysprof-ui/sysprof-memory-aid.h b/src/libsysprof-ui/sysprof-memory-aid.h deleted file mode 100644 index d33a1d8c..00000000 --- a/src/libsysprof-ui/sysprof-memory-aid.h +++ /dev/null @@ -1,33 +0,0 @@ -/* sysprof-memory-aid.h - * - * Copyright 2019 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-aid.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_MEMORY_AID (sysprof_memory_aid_get_type()) - -G_DECLARE_FINAL_TYPE (SysprofMemoryAid, sysprof_memory_aid, SYSPROF, MEMORY_AID, SysprofAid) - -SysprofAid *sysprof_memory_aid_new (void); - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-memprof-aid.c b/src/libsysprof-ui/sysprof-memprof-aid.c deleted file mode 100644 index 21325177..00000000 --- a/src/libsysprof-ui/sysprof-memprof-aid.c +++ /dev/null @@ -1,226 +0,0 @@ -/* sysprof-memprof-aid.c - * - * Copyright 2019 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 - */ - -#define G_LOG_DOMAIN "sysprof-memprof-aid" - -#include "config.h" - -#include - -#include "sysprof-memprof-aid.h" -#include "sysprof-memprof-page.h" -#include "sysprof-memprof-source.h" -#include "sysprof-memprof-visualizer.h" - -struct _SysprofMemprofAid -{ - SysprofAid parent_instance; -}; - -G_DEFINE_TYPE (SysprofMemprofAid, sysprof_memprof_aid, SYSPROF_TYPE_AID) - -typedef struct -{ - SysprofCaptureCursor *cursor; - SysprofDisplay *display; - guint has_allocs : 1; -} Present; - -static void -present_free (gpointer data) -{ - Present *p = data; - - g_clear_pointer (&p->cursor, sysprof_capture_cursor_unref); - g_clear_object (&p->display); - g_slice_free (Present, p); -} - -static void -on_group_activated_cb (SysprofVisualizerGroup *group, - SysprofPage *page) -{ - SysprofDisplay *display; - - g_assert (SYSPROF_IS_VISUALIZER_GROUP (group)); - g_assert (SYSPROF_IS_PAGE (page)); - - display = SYSPROF_DISPLAY (gtk_widget_get_ancestor (GTK_WIDGET (page), SYSPROF_TYPE_DISPLAY)); - sysprof_display_set_visible_page (display, page); -} - -SysprofAid * -sysprof_memprof_aid_new (void) -{ - return g_object_new (SYSPROF_TYPE_MEMPROF_AID, NULL); -} - -static void -sysprof_memprof_aid_prepare (SysprofAid *self, - SysprofProfiler *profiler) -{ -#ifdef __linux__ - g_autoptr(SysprofSource) source = NULL; - - g_assert (SYSPROF_IS_MEMPROF_AID (self)); - g_assert (SYSPROF_IS_PROFILER (profiler)); - - source = sysprof_memprof_source_new (); - sysprof_profiler_add_source (profiler, source); -#endif -} - -static bool -discover_samples_cb (const SysprofCaptureFrame *frame, - gpointer user_data) -{ - Present *p = user_data; - - g_assert (frame != NULL); - g_assert (p != NULL); - - if (frame->type == SYSPROF_CAPTURE_FRAME_ALLOCATION) - { - p->has_allocs = TRUE; - return FALSE; - } - - return TRUE; -} - -static void -sysprof_memprof_aid_present_worker (GTask *task, - gpointer source_object, - gpointer task_data, - GCancellable *cancellable) -{ - Present *p = task_data; - - g_assert (G_IS_TASK (task)); - g_assert (SYSPROF_IS_MEMPROF_AID (source_object)); - g_assert (p != NULL); - g_assert (p->cursor != NULL); - g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); - - sysprof_capture_cursor_foreach (p->cursor, discover_samples_cb, p); - g_task_return_boolean (task, TRUE); -} - -static void -sysprof_memprof_aid_present_async (SysprofAid *aid, - SysprofCaptureReader *reader, - SysprofDisplay *display, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - static const SysprofCaptureFrameType types[] = { SYSPROF_CAPTURE_FRAME_ALLOCATION }; - g_autoptr(SysprofCaptureCondition) condition = NULL; - g_autoptr(SysprofCaptureCursor) cursor = NULL; - g_autoptr(GTask) task = NULL; - Present present; - - g_assert (SYSPROF_IS_MEMPROF_AID (aid)); - g_assert (reader != NULL); - g_assert (SYSPROF_IS_DISPLAY (display)); - g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); - - condition = sysprof_capture_condition_new_where_type_in (1, types); - cursor = sysprof_capture_cursor_new (reader); - sysprof_capture_cursor_add_condition (cursor, g_steal_pointer (&condition)); - - present.cursor = g_steal_pointer (&cursor); - present.display = g_object_ref (display); - - task = g_task_new (aid, cancellable, callback, user_data); - g_task_set_source_tag (task, sysprof_memprof_aid_present_async); - g_task_set_task_data (task, - g_slice_dup (Present, &present), - present_free); - g_task_run_in_thread (task, sysprof_memprof_aid_present_worker); -} - -static gboolean -sysprof_memprof_aid_present_finish (SysprofAid *aid, - GAsyncResult *result, - GError **error) -{ - Present *p; - - g_assert (SYSPROF_IS_MEMPROF_AID (aid)); - g_assert (G_IS_TASK (result)); - - p = g_task_get_task_data (G_TASK (result)); - - if (p->has_allocs) - { - SysprofVisualizerGroup *group; - SysprofVisualizer *row; - SysprofPage *page; - - group = g_object_new (SYSPROF_TYPE_VISUALIZER_GROUP, - "can-focus", TRUE, - "has-page", TRUE, - "priority", -300, - "title", _("Memory"), - "visible", TRUE, - NULL); - - row = sysprof_memprof_visualizer_new (FALSE); - sysprof_visualizer_group_insert (group, row, 0, FALSE); - - row = sysprof_memprof_visualizer_new (TRUE); - sysprof_visualizer_group_insert (group, row, 1, FALSE); - - page = g_object_new (SYSPROF_TYPE_MEMPROF_PAGE, - "title", _("Memory Allocations"), - "vexpand", TRUE, - "visible", TRUE, - NULL); - sysprof_display_add_page (p->display, page); - - g_signal_connect_object (group, - "group-activated", - G_CALLBACK (on_group_activated_cb), - page, - 0); - - sysprof_display_add_group (p->display, group); - } - - return g_task_propagate_boolean (G_TASK (result), error); -} - -static void -sysprof_memprof_aid_class_init (SysprofMemprofAidClass *klass) -{ - SysprofAidClass *aid_class = SYSPROF_AID_CLASS (klass); - - aid_class->prepare = sysprof_memprof_aid_prepare; - aid_class->present_async = sysprof_memprof_aid_present_async; - aid_class->present_finish = sysprof_memprof_aid_present_finish; -} - -static void -sysprof_memprof_aid_init (SysprofMemprofAid *self) -{ - sysprof_aid_set_display_name (SYSPROF_AID (self), _("Track Allocations")); - sysprof_aid_set_icon_name (SYSPROF_AID (self), "org.gnome.Sysprof-symbolic"); -} diff --git a/src/libsysprof-ui/sysprof-memprof-aid.h b/src/libsysprof-ui/sysprof-memprof-aid.h deleted file mode 100644 index b5294d57..00000000 --- a/src/libsysprof-ui/sysprof-memprof-aid.h +++ /dev/null @@ -1,33 +0,0 @@ -/* sysprof-memprof-aid.h - * - * Copyright 2019 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-aid.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_MEMPROF_AID (sysprof_memprof_aid_get_type()) - -G_DECLARE_FINAL_TYPE (SysprofMemprofAid, sysprof_memprof_aid, SYSPROF, MEMPROF_AID, SysprofAid) - -SysprofAid *sysprof_memprof_aid_new (void); - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-memprof-page.c b/src/libsysprof-ui/sysprof-memprof-page.c deleted file mode 100644 index 00e98961..00000000 --- a/src/libsysprof-ui/sysprof-memprof-page.c +++ /dev/null @@ -1,1552 +0,0 @@ -/* sysprof-memprof-page.c - * - * Copyright 2016-2019 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 - */ - -/* Sysprof -- Sampling, systemwide CPU profiler - * Copyright 2004, Red Hat, Inc. - * Copyright 2004, 2005, 2006, Soeren Sandmann - * - * 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 2 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, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include "config.h" - -#include - -#include "../stackstash.h" - -#include "egg-paned-private.h" - -#include "sysprof-cell-renderer-percent.h" -#include "sysprof-memprof-page.h" -#include "sysprof-profile.h" - -typedef struct -{ - SysprofMemprofProfile *profile; - - GtkTreeView *callers_view; - GtkTreeView *functions_view; - GtkTreeView *descendants_view; - GtkTreeViewColumn *descendants_name_column; - GtkTreeViewColumn *function_size_column; - GtkCellRendererText *function_size_cell; - GtkStack *stack; - GtkToggleButton *summary; - GtkToggleButton *all_allocs; - GtkToggleButton *temp_allocs; - GtkToggleButton *leaked_allocs_button; - GtkLabel *temp_allocs_count; - GtkLabel *num_allocs; - GtkLabel *leaked_allocs; - GtkListBox *by_size; - GtkWidget *callgraph; - GtkWidget *summary_page; - GtkWidget *loading_state; - GtkWidget *empty_state; - - GCancellable *cancellable; - - GQueue *history; - - SysprofMemprofMode mode; - - guint profile_size; - guint loading; -} SysprofMemprofPagePrivate; - -G_DEFINE_TYPE_WITH_PRIVATE (SysprofMemprofPage, sysprof_memprof_page, SYSPROF_TYPE_PAGE) - -enum { - PROP_0, - PROP_PROFILE, - N_PROPS -}; - -enum { - GO_PREVIOUS, - N_SIGNALS -}; - -enum { - COLUMN_NAME, - COLUMN_SELF, - COLUMN_TOTAL, - COLUMN_POINTER, - COLUMN_SIZE, -}; - -static void sysprof_memprof_page_update_descendants (SysprofMemprofPage *self, - StackNode *node); - -static GParamSpec *properties [N_PROPS]; -static guint signals [N_SIGNALS]; - -static guint -sysprof_memprof_page_get_profile_size (SysprofMemprofPage *self) -{ - SysprofMemprofPagePrivate *priv = sysprof_memprof_page_get_instance_private (self); - StackStash *stash; - StackNode *node; - guint size = 0; - - g_assert (SYSPROF_IS_MEMPROF_PAGE (self)); - - if (priv->profile_size != 0) - return priv->profile_size; - - if (priv->profile == NULL) - return 0; - - if (NULL == (stash = sysprof_memprof_profile_get_stash (priv->profile))) - return 0; - - for (node = stack_stash_get_root (stash); node != NULL; node = node->siblings) - size += node->total; - - priv->profile_size = size; - - return size; -} - -static void -build_functions_store (StackNode *node, - gpointer user_data) -{ - struct { - GtkListStore *store; - gdouble profile_size; - } *state = user_data; - GtkTreeIter iter; - const StackNode *n; - guint64 size = 0; - guint64 total = 0; - - g_assert (state != NULL); - g_assert (GTK_IS_LIST_STORE (state->store)); - - for (n = node; n != NULL; n = n->next) - { - size += n->size; - if (n->toplevel) - total += n->total; - } - - gtk_list_store_append (state->store, &iter); - gtk_list_store_set (state->store, &iter, - COLUMN_NAME, U64_TO_POINTER (node->data), - COLUMN_SELF, 100.0 * size / state->profile_size, - COLUMN_TOTAL, 100.0 * total / state->profile_size, - COLUMN_POINTER, node, - COLUMN_SIZE, (guint64)total, - -1); - -} - -static void -update_summary (SysprofMemprofPage *self, - SysprofMemprofProfile *profile) -{ - SysprofMemprofPagePrivate *priv = sysprof_memprof_page_get_instance_private (self); - SysprofMemprofStats stats; - g_autoptr(GString) str = NULL; - GtkWidget *child; - - g_assert (SYSPROF_IS_MEMPROF_PAGE (self)); - g_assert (SYSPROF_IS_MEMPROF_PROFILE (profile)); - - sysprof_memprof_profile_get_stats (profile, &stats); - - str = g_string_new (NULL); - - g_string_append_printf (str, "%"G_GINT64_FORMAT, stats.n_allocs); - gtk_label_set_label (priv->num_allocs, str->str); - g_string_truncate (str, 0); - - g_string_append_printf (str, "%"G_GINT64_FORMAT, stats.leaked_allocs); - gtk_label_set_label (priv->leaked_allocs, str->str); - g_string_truncate (str, 0); - - g_string_append_printf (str, "%"G_GINT64_FORMAT, stats.temp_allocs); - gtk_label_set_label (priv->temp_allocs_count, str->str); - g_string_truncate (str, 0); - - while ((child = gtk_widget_get_first_child (GTK_WIDGET (priv->by_size)))) - gtk_list_box_remove (priv->by_size, child); - - for (guint i = 0; i < G_N_ELEMENTS (stats.by_size); i++) - { - g_autofree gchar *prevstr = NULL; - g_autofree gchar *sizestr = NULL; - g_autofree gchar *title_str = NULL; - g_autofree gchar *subtitle_str = NULL; - g_autofree gchar *allocstr = NULL; - g_autofree gchar *tempstr = NULL; - g_autofree gchar *allstr = NULL; - GtkWidget *row; - GtkWidget *title; - GtkWidget *subtitle; - GtkWidget *prog; - GtkWidget *box; - - if (stats.by_size[i].n_allocs == 0) - continue; - - row = gtk_list_box_row_new (); - title = gtk_label_new (NULL); - subtitle = gtk_label_new (NULL); - prog = gtk_level_bar_new_for_interval (0, stats.n_allocs); - box = g_object_new (GTK_TYPE_BOX, - "orientation", GTK_ORIENTATION_VERTICAL, - "spacing", 6, - "margin-top", 6, - "margin-start", 6, - "margin-bottom", 6, - "margin-end", 6, - NULL); - - sizestr = g_format_size_full (stats.by_size[i].bucket, G_FORMAT_SIZE_IEC_UNITS); - if (i == 0) - { - title_str = g_strdup_printf ("≤ %s", sizestr); - } - else - { - /* translators: %s is replaced with a memory size such as "32 bytes" */ - prevstr = g_format_size_full (stats.by_size[i-1].bucket, G_FORMAT_SIZE_IEC_UNITS); - /* translators: %s is replaced with the the lower and upper bound memory sizes in bytes */ - title_str = g_strdup_printf (_("> %s to %s"), prevstr, sizestr); - } - - gtk_label_set_label (GTK_LABEL (title), title_str); - gtk_label_set_xalign (GTK_LABEL (title), 0); - gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (title)), "dim-label"); - - gtk_widget_set_margin_start (box, 6); - gtk_widget_set_margin_end (box, 6); - - gtk_widget_set_margin_top (prog, 1); - gtk_widget_set_margin_bottom (prog, 1); - - allocstr = g_strdup_printf ("%"G_GINT64_FORMAT, stats.by_size[i].n_allocs); - tempstr = g_strdup_printf ("%"G_GINT64_FORMAT, stats.by_size[i].temp_allocs); - allstr = g_format_size_full (stats.by_size[i].allocated, - G_FORMAT_SIZE_IEC_UNITS); - subtitle_str = g_strdup_printf ("%s allocations, %s temporary, %s", - allocstr, tempstr, allstr); - - gtk_label_set_label (GTK_LABEL (subtitle), subtitle_str); - gtk_label_set_xalign (GTK_LABEL (subtitle), 0); - -#if 0 - /* TODO: Make this chunked by [temp][rest]... */ - gtk_level_bar_add_offset_value (GTK_LEVEL_BAR (prog), - GTK_LEVEL_BAR_OFFSET_HIGH, - stats.by_size[i].temp_allocs); - gtk_level_bar_add_offset_value (GTK_LEVEL_BAR (prog), - GTK_LEVEL_BAR_OFFSET_LOW, - stats.by_size[i].n_allocs); -#endif - gtk_level_bar_set_value (GTK_LEVEL_BAR (prog), - stats.by_size[i].n_allocs); - - gtk_list_box_row_set_child (GTK_LIST_BOX_ROW (row), box); - gtk_box_append (GTK_BOX (box), title); - gtk_box_append (GTK_BOX (box), prog); - gtk_box_append (GTK_BOX (box), subtitle); - gtk_list_box_append (priv->by_size, row); - } -} - -static void -sysprof_memprof_page_load (SysprofMemprofPage *self, - SysprofMemprofProfile *profile) -{ - SysprofMemprofPagePrivate *priv = sysprof_memprof_page_get_instance_private (self); - GtkListStore *functions; - StackStash *stash; - StackNode *n; - GtkTreeIter iter; - struct { - GtkListStore *store; - gdouble profile_size; - } state = { 0 }; - - g_assert (SYSPROF_IS_MEMPROF_PAGE (self)); - g_assert (SYSPROF_IS_MEMPROF_PROFILE (profile)); - - /* - * TODO: This is probably the type of thing we want to do off the main - * thread. We should be able to build the tree models off thread - * and then simply apply them on the main thread. - * - * In the mean time, we should set the state of the widget to - * insensitive and give some indication of loading progress. - */ - - if (!g_set_object (&priv->profile, profile)) - return; - - update_summary (self, profile); - - if (sysprof_memprof_profile_is_empty (profile)) - { - gtk_stack_set_visible_child (priv->stack, priv->summary_page); - return; - } - - stash = sysprof_memprof_profile_get_stash (profile); - - for (n = stack_stash_get_root (stash); n; n = n->siblings) - state.profile_size += n->total; - - functions = gtk_list_store_new (5, - G_TYPE_STRING, - G_TYPE_DOUBLE, - G_TYPE_DOUBLE, - G_TYPE_POINTER, - G_TYPE_UINT64); - - state.store = functions; - stack_stash_foreach_by_address (stash, build_functions_store, &state); - - gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (functions), - COLUMN_TOTAL, - GTK_SORT_DESCENDING); - - gtk_tree_view_set_model (priv->functions_view, GTK_TREE_MODEL (functions)); - gtk_tree_view_set_model (priv->callers_view, NULL); - gtk_tree_view_set_model (priv->descendants_view, NULL); - - if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (functions), &iter)) - { - GtkTreeSelection *selection; - - selection = gtk_tree_view_get_selection (priv->functions_view); - gtk_tree_selection_select_iter (selection, &iter); - } - - gtk_stack_set_visible_child (priv->stack, priv->callgraph); - - g_clear_object (&functions); -} - -void -_sysprof_memprof_page_set_failed (SysprofMemprofPage *self) -{ - SysprofMemprofPagePrivate *priv = sysprof_memprof_page_get_instance_private (self); - - g_return_if_fail (SYSPROF_IS_MEMPROF_PAGE (self)); - - gtk_stack_set_visible_child (priv->stack, priv->empty_state); -} - -static void -sysprof_memprof_page_unload (SysprofMemprofPage *self) -{ - SysprofMemprofPagePrivate *priv = sysprof_memprof_page_get_instance_private (self); - - g_assert (SYSPROF_IS_MEMPROF_PAGE (self)); - g_assert (SYSPROF_IS_MEMPROF_PROFILE (priv->profile)); - - g_queue_clear (priv->history); - g_clear_object (&priv->profile); - priv->profile_size = 0; - - gtk_tree_view_set_model (priv->callers_view, NULL); - gtk_tree_view_set_model (priv->functions_view, NULL); - gtk_tree_view_set_model (priv->descendants_view, NULL); - - gtk_stack_set_visible_child (priv->stack, priv->empty_state); -} - -/** - * sysprof_memprof_page_get_profile: - * - * Returns: (transfer none): An #SysprofMemprofProfile. - */ -SysprofMemprofProfile * -sysprof_memprof_page_get_profile (SysprofMemprofPage *self) -{ - SysprofMemprofPagePrivate *priv = sysprof_memprof_page_get_instance_private (self); - - g_return_val_if_fail (SYSPROF_IS_MEMPROF_PAGE (self), NULL); - - return priv->profile; -} - -void -sysprof_memprof_page_set_profile (SysprofMemprofPage *self, - SysprofMemprofProfile *profile) -{ - SysprofMemprofPagePrivate *priv = sysprof_memprof_page_get_instance_private (self); - - g_return_if_fail (SYSPROF_IS_MEMPROF_PAGE (self)); - g_return_if_fail (!profile || SYSPROF_IS_MEMPROF_PROFILE (profile)); - - if (profile != priv->profile) - { - if (priv->profile) - sysprof_memprof_page_unload (self); - - if (profile) - sysprof_memprof_page_load (self, profile); - - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_PROFILE]); - } -} - -static void -sysprof_memprof_page_expand_descendants (SysprofMemprofPage *self) -{ - SysprofMemprofPagePrivate *priv = sysprof_memprof_page_get_instance_private (self); - GtkTreeModel *model; - GList *all_paths = NULL; - GtkTreePath *first_path; - GtkTreeIter iter; - gdouble top_value = 0; - gint max_rows = 40; /* FIXME */ - gint n_rows; - - g_assert (SYSPROF_IS_MEMPROF_PAGE (self)); - - model = gtk_tree_view_get_model (priv->descendants_view); - first_path = gtk_tree_path_new_first (); - all_paths = g_list_prepend (all_paths, first_path); - n_rows = 1; - - gtk_tree_model_get_iter (model, &iter, first_path); - gtk_tree_model_get (model, &iter, - COLUMN_TOTAL, &top_value, - -1); - - while ((all_paths != NULL) && (n_rows < max_rows)) - { - GtkTreeIter best_iter; - GtkTreePath *best_path = NULL; - GList *list; - gdouble best_value = 0.0; - gint n_children; - gint i; - - for (list = all_paths; list != NULL; list = list->next) - { - GtkTreePath *path = list->data; - - g_assert (path != NULL); - - if (gtk_tree_model_get_iter (model, &iter, path)) - { - gdouble value; - - gtk_tree_model_get (model, &iter, - COLUMN_TOTAL, &value, - -1); - - if (value >= best_value) - { - best_value = value; - best_path = path; - best_iter = iter; - } - } - } - - n_children = gtk_tree_model_iter_n_children (model, &best_iter); - - if ((n_children > 0) && - ((best_value / top_value) > 0.04) && - ((n_children + gtk_tree_path_get_depth (best_path)) / (gdouble)max_rows) < (best_value / top_value)) - { - gtk_tree_view_expand_row (priv->descendants_view, best_path, FALSE); - n_rows += n_children; - - if (gtk_tree_path_get_depth (best_path) < 4) - { - GtkTreePath *path; - - path = gtk_tree_path_copy (best_path); - gtk_tree_path_down (path); - - for (i = 0; i < n_children; i++) - { - all_paths = g_list_prepend (all_paths, path); - - path = gtk_tree_path_copy (path); - gtk_tree_path_next (path); - } - - gtk_tree_path_free (path); - } - } - - all_paths = g_list_remove (all_paths, best_path); - - /* Always expand at least once */ - if ((all_paths == NULL) && (n_rows == 1)) - gtk_tree_view_expand_row (priv->descendants_view, best_path, FALSE); - - gtk_tree_path_free (best_path); - } - - g_list_free_full (all_paths, (GDestroyNotify)gtk_tree_path_free); -} - -typedef struct -{ - StackNode *node; - const gchar *name; - guint self; - guint total; -} Caller; - -static Caller * -caller_new (StackNode *node) -{ - Caller *c; - - c = g_slice_new (Caller); - c->name = U64_TO_POINTER (node->data); - c->self = 0; - c->total = 0; - c->node = node; - - return c; -} - -static void -caller_free (gpointer data) -{ - Caller *c = data; - g_slice_free (Caller, c); -} - -static void -sysprof_memprof_page_function_selection_changed (SysprofMemprofPage *self, - GtkTreeSelection *selection) -{ - SysprofMemprofPagePrivate *priv = sysprof_memprof_page_get_instance_private (self); - GtkTreeModel *model = NULL; - GtkTreeIter iter; - GtkListStore *callers_store; - g_autoptr(GHashTable) callers = NULL; - g_autoptr(GHashTable) processed = NULL; - StackNode *callees = NULL; - StackNode *node; - - g_assert (SYSPROF_IS_MEMPROF_PAGE (self)); - g_assert (GTK_IS_TREE_SELECTION (selection)); - - if (!gtk_tree_selection_get_selected (selection, &model, &iter)) - { - gtk_tree_view_set_model (priv->callers_view, NULL); - gtk_tree_view_set_model (priv->descendants_view, NULL); - return; - } - - gtk_tree_model_get (model, &iter, - COLUMN_POINTER, &callees, - -1); - - sysprof_memprof_page_update_descendants (self, callees); - - callers_store = gtk_list_store_new (5, - G_TYPE_STRING, - G_TYPE_DOUBLE, - G_TYPE_DOUBLE, - G_TYPE_POINTER, - G_TYPE_UINT64); - - callers = g_hash_table_new_full (NULL, NULL, NULL, caller_free); - processed = g_hash_table_new (NULL, NULL); - - for (node = callees; node != NULL; node = node->next) - { - Caller *c; - - if (!node->parent) - continue; - - c = g_hash_table_lookup (callers, U64_TO_POINTER (node->parent->data)); - - if (c == NULL) - { - c = caller_new (node->parent); - g_hash_table_insert (callers, (gpointer)c->name, c); - } - } - - for (node = callees; node != NULL; node = node->next) - { - StackNode *top_caller = node->parent; - StackNode *top_callee = node; - StackNode *n; - Caller *c; - - if (!node->parent) - continue; - - /* - * We could have a situation where the function was called in a - * reentrant fashion, so we want to take the top-most match in the - * stack. - */ - for (n = node; n && n->parent; n = n->parent) - { - if (n->data == node->data && n->parent->data == node->parent->data) - { - top_caller = n->parent; - top_callee = n; - } - } - - c = g_hash_table_lookup (callers, U64_TO_POINTER (node->parent->data)); - - g_assert (c != NULL); - - if (!g_hash_table_lookup (processed, top_caller)) - { - c->total += top_callee->total; - g_hash_table_insert (processed, top_caller, top_caller); - } - - c->self += node->size; - } - - { - GHashTableIter hiter; - gpointer key, value; - guint64 size = 0; - - size = MAX (1, sysprof_memprof_page_get_profile_size (self)); - - g_hash_table_iter_init (&hiter, callers); - - while (g_hash_table_iter_next (&hiter, &key, &value)) - { - Caller *c = value; - - gtk_list_store_append (callers_store, &iter); - gtk_list_store_set (callers_store, &iter, - COLUMN_NAME, c->name, - COLUMN_SELF, c->self * 100.0 / size, - COLUMN_TOTAL, c->total * 100.0 / size, - COLUMN_POINTER, c->node, - COLUMN_SIZE, (guint64)c->total, - -1); - } - } - - gtk_tree_view_set_model (priv->callers_view, GTK_TREE_MODEL (callers_store)); - gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (callers_store), - COLUMN_TOTAL, - GTK_SORT_DESCENDING); - - g_clear_object (&callers_store); -} - -static void -sysprof_memprof_page_set_node (SysprofMemprofPage *self, - StackNode *node) -{ - SysprofMemprofPagePrivate *priv = sysprof_memprof_page_get_instance_private (self); - GtkTreeModel *model; - GtkTreeIter iter; - - g_assert (SYSPROF_IS_MEMPROF_PAGE (self)); - g_assert (node != NULL); - - if (priv->profile == NULL) - return; - - model = gtk_tree_view_get_model (priv->functions_view); - - if (gtk_tree_model_get_iter_first (model, &iter)) - { - do - { - StackNode *item = NULL; - - gtk_tree_model_get (model, &iter, - COLUMN_POINTER, &item, - -1); - - if (item != NULL && item->data == node->data) - { - GtkTreeSelection *selection; - - selection = gtk_tree_view_get_selection (priv->functions_view); - gtk_tree_selection_select_iter (selection, &iter); - - break; - } - } - while (gtk_tree_model_iter_next (model, &iter)); - } -} - -static void -sysprof_memprof_page_descendant_activated (SysprofMemprofPage *self, - GtkTreePath *path, - GtkTreeViewColumn *column, - GtkTreeView *tree_view) -{ - GtkTreeModel *model; - StackNode *node = NULL; - GtkTreeIter iter; - - g_assert (SYSPROF_IS_MEMPROF_PAGE (self)); - g_assert (GTK_IS_TREE_VIEW (tree_view)); - g_assert (path != NULL); - g_assert (GTK_IS_TREE_VIEW_COLUMN (column)); - - model = gtk_tree_view_get_model (tree_view); - - if (!gtk_tree_model_get_iter (model, &iter, path)) - return; - - gtk_tree_model_get (model, &iter, - COLUMN_POINTER, &node, - -1); - - if (node != NULL) - sysprof_memprof_page_set_node (self, node); -} - -static void -sysprof_memprof_page_caller_activated (SysprofMemprofPage *self, - GtkTreePath *path, - GtkTreeViewColumn *column, - GtkTreeView *tree_view) -{ - GtkTreeModel *model; - StackNode *node = NULL; - GtkTreeIter iter; - - g_assert (SYSPROF_IS_MEMPROF_PAGE (self)); - g_assert (GTK_IS_TREE_VIEW (tree_view)); - g_assert (path != NULL); - g_assert (GTK_IS_TREE_VIEW_COLUMN (column)); - - model = gtk_tree_view_get_model (tree_view); - - if (!gtk_tree_model_get_iter (model, &iter, path)) - return; - - gtk_tree_model_get (model, &iter, - COLUMN_POINTER, &node, - -1); - - if (node != NULL) - sysprof_memprof_page_set_node (self, node); -} - -static void -sysprof_memprof_page_size_data_func (GtkTreeViewColumn *column, - GtkCellRenderer *cell, - GtkTreeModel *model, - GtkTreeIter *iter, - gpointer data) -{ - g_autofree gchar *size_str = NULL; - guint64 size; - - gtk_tree_model_get (model, iter, COLUMN_SIZE, &size, -1); - if (size) - size_str = g_format_size_full (size, G_FORMAT_SIZE_IEC_UNITS); - g_object_set (cell, "text", size_str, NULL); -} - -static void -sysprof_memprof_page_tag_data_func (GtkTreeViewColumn *column, - GtkCellRenderer *cell, - GtkTreeModel *model, - GtkTreeIter *iter, - gpointer data) -{ - SysprofMemprofPage *self = data; - SysprofMemprofPagePrivate *priv = sysprof_memprof_page_get_instance_private (self); - StackNode *node = NULL; - const gchar *str = NULL; - - if (priv->profile == NULL) - return; - - gtk_tree_model_get (model, iter, COLUMN_POINTER, &node, -1); - - if (node && node->data) - { - GQuark tag; - - tag = sysprof_memprof_profile_get_tag (priv->profile, GSIZE_TO_POINTER (node->data)); - if (tag != 0) - str = g_quark_to_string (tag); - } - - g_object_set (cell, "text", str, NULL); -} - -static void -sysprof_memprof_page_real_go_previous (SysprofMemprofPage *self) -{ - SysprofMemprofPagePrivate *priv = sysprof_memprof_page_get_instance_private (self); - StackNode *node; - - g_assert (SYSPROF_IS_MEMPROF_PAGE (self)); - - node = g_queue_pop_head (priv->history); - - if (NULL != (node = g_queue_peek_head (priv->history))) - sysprof_memprof_page_set_node (self, node); -} - -static void -descendants_view_move_cursor_cb (GtkTreeView *descendants_view, - GtkMovementStep step, - int direction, - gpointer user_data) -{ - if (step == GTK_MOVEMENT_VISUAL_POSITIONS) - { - GtkTreePath *path; - - gtk_tree_view_get_cursor (descendants_view, &path, NULL); - - if (direction == 1) - { - gtk_tree_view_expand_row (descendants_view, path, FALSE); - g_signal_stop_emission_by_name (descendants_view, "move-cursor"); - } - else if (direction == -1) - { - gtk_tree_view_collapse_row (descendants_view, path); - g_signal_stop_emission_by_name (descendants_view, "move-cursor"); - } - - gtk_tree_path_free (path); - } -} - -static void -copy_tree_view_selection_cb (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer data) -{ - g_autofree gchar *name = NULL; - g_autofree gchar *size_str = NULL; - gchar tstr[16]; - GString *str = data; - gdouble total; - guint64 size; - gint depth; - - g_assert (GTK_IS_TREE_MODEL (model)); - g_assert (path != NULL); - g_assert (iter != NULL); - g_assert (str != NULL); - - depth = gtk_tree_path_get_depth (path); - gtk_tree_model_get (model, iter, - COLUMN_NAME, &name, - COLUMN_TOTAL, &total, - COLUMN_SIZE, &size, - -1); - - size_str = g_format_size_full (size, G_FORMAT_SIZE_IEC_UNITS); - g_snprintf (tstr, sizeof tstr, "%.2lf%%", total); - - g_string_append_printf (str, "[%12s] [%8s] ", size_str, tstr); - - for (gint i = 1; i < depth; i++) - g_string_append (str, " "); - g_string_append (str, name); - g_string_append_c (str, '\n'); -} - -static void -copy_tree_view_selection (GtkTreeView *tree_view) -{ - g_autoptr(GString) str = NULL; - GdkClipboard *clipboard; - - g_assert (GTK_IS_TREE_VIEW (tree_view)); - - str = g_string_new (" ALLOCATED TOTAL FUNCTION\n"); - gtk_tree_selection_selected_foreach (gtk_tree_view_get_selection (tree_view), - copy_tree_view_selection_cb, - str); - - clipboard = gtk_widget_get_clipboard (GTK_WIDGET (tree_view)); - gdk_clipboard_set_text (clipboard, str->str); -} - -static void -sysprof_memprof_page_copy_cb (GtkWidget *widget, - const char *action_name, - GVariant *param) -{ - SysprofMemprofPage *self = (SysprofMemprofPage *)widget; - SysprofMemprofPagePrivate *priv = sysprof_memprof_page_get_instance_private (self); - GtkWidget *focus; - GtkRoot *toplevel; - - g_assert (SYSPROF_IS_MEMPROF_PAGE (self)); - - if (!(toplevel = gtk_widget_get_root (widget)) || - !GTK_IS_ROOT (toplevel) || - !(focus = gtk_root_get_focus (toplevel))) - return; - - if (focus == GTK_WIDGET (priv->descendants_view)) - copy_tree_view_selection (priv->descendants_view); - else if (focus == GTK_WIDGET (priv->callers_view)) - copy_tree_view_selection (priv->callers_view); - else if (focus == GTK_WIDGET (priv->functions_view)) - copy_tree_view_selection (priv->functions_view); -} - -static void -sysprof_memprof_page_generate_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - SysprofProfile *profile = (SysprofProfile *)object; - SysprofMemprofPage *self; - g_autoptr(GTask) task = user_data; - g_autoptr(GError) error = NULL; - - g_assert (SYSPROF_IS_PROFILE (profile)); - g_assert (G_IS_ASYNC_RESULT (result)); - g_assert (G_IS_TASK (task)); - - self = g_task_get_source_object (task); - - if (!sysprof_profile_generate_finish (profile, result, &error)) - g_task_return_error (task, g_error_copy (error)); - else - sysprof_memprof_page_set_profile (self, SYSPROF_MEMPROF_PROFILE (profile)); -} - -static void -sysprof_memprof_page_load_async (SysprofPage *page, - SysprofCaptureReader *reader, - SysprofSelection *selection, - SysprofCaptureCondition *filter, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - SysprofMemprofPage *self = (SysprofMemprofPage *)page; - SysprofMemprofPagePrivate *priv = sysprof_memprof_page_get_instance_private (self); - g_autoptr(SysprofCaptureReader) copy = NULL; - g_autoptr(SysprofProfile) profile = NULL; - g_autoptr(GTask) task = NULL; - - g_assert (SYSPROF_IS_MEMPROF_PAGE (self)); - g_assert (reader != NULL); - g_assert (SYSPROF_IS_SELECTION (selection)); - g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); - - g_cancellable_cancel (priv->cancellable); - - if (cancellable == NULL) - cancellable = priv->cancellable = g_cancellable_new (); - else - g_set_object (&priv->cancellable, cancellable); - - gtk_stack_set_visible_child (priv->stack, priv->loading_state); - - task = g_task_new (self, cancellable, callback, user_data); - g_task_set_source_tag (task, sysprof_memprof_page_load_async); - - copy = sysprof_capture_reader_copy (reader); - - profile = sysprof_memprof_profile_new_with_selection (selection); - sysprof_memprof_profile_set_mode (SYSPROF_MEMPROF_PROFILE (profile), priv->mode); - sysprof_profile_set_reader (profile, reader); - sysprof_profile_generate (profile, - cancellable, - sysprof_memprof_page_generate_cb, - g_steal_pointer (&task)); -} - -static gboolean -sysprof_memprof_page_load_finish (SysprofPage *page, - GAsyncResult *result, - GError **error) -{ - g_return_val_if_fail (SYSPROF_IS_MEMPROF_PAGE (page), FALSE); - g_return_val_if_fail (G_IS_TASK (result), FALSE); - - return g_task_propagate_boolean (G_TASK (result), error); -} - -static void -do_allocs (SysprofMemprofPage *self, - SysprofMemprofMode mode) -{ - SysprofMemprofPagePrivate *priv = sysprof_memprof_page_get_instance_private (self); - - g_assert (SYSPROF_IS_MEMPROF_PAGE (self)); - - priv->mode = mode; - sysprof_page_reload (SYSPROF_PAGE (self)); -} - -static void -mode_notify_active (SysprofMemprofPage *self, - GParamSpec *pspec, - GtkToggleButton *button) -{ - SysprofMemprofPagePrivate *priv = sysprof_memprof_page_get_instance_private (self); - - g_assert (SYSPROF_IS_MEMPROF_PAGE (self)); - g_assert (GTK_IS_TOGGLE_BUTTON (button)); - - if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) - { - if (button == priv->summary) - do_allocs (self, SYSPROF_MEMPROF_MODE_SUMMARY); - else if (button == priv->all_allocs) - do_allocs (self, SYSPROF_MEMPROF_MODE_ALL_ALLOCS); - else if (button == priv->temp_allocs) - do_allocs (self, SYSPROF_MEMPROF_MODE_TEMP_ALLOCS); - else if (button == priv->leaked_allocs_button) - do_allocs (self, SYSPROF_MEMPROF_MODE_LEAKED_ALLOCS); - } -} - -static void -sep_header_func (GtkListBoxRow *row, - GtkListBoxRow *before, - gpointer user_data) -{ - if (before != NULL) - gtk_list_box_row_set_header (row, - g_object_new (GTK_TYPE_SEPARATOR, - "orientation", GTK_ORIENTATION_HORIZONTAL, - "visible", TRUE, - NULL)); -} - -static void -sysprof_memprof_page_finalize (GObject *object) -{ - SysprofMemprofPage *self = (SysprofMemprofPage *)object; - SysprofMemprofPagePrivate *priv = sysprof_memprof_page_get_instance_private (self); - - g_clear_pointer (&priv->history, g_queue_free); - g_clear_object (&priv->profile); - g_clear_object (&priv->cancellable); - - G_OBJECT_CLASS (sysprof_memprof_page_parent_class)->finalize (object); -} - -static void -sysprof_memprof_page_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - SysprofMemprofPage *self = SYSPROF_MEMPROF_PAGE (object); - SysprofMemprofPagePrivate *priv = sysprof_memprof_page_get_instance_private (self); - - switch (prop_id) - { - case PROP_PROFILE: - g_value_set_object (value, priv->profile); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_memprof_page_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - SysprofMemprofPage *self = SYSPROF_MEMPROF_PAGE (object); - - switch (prop_id) - { - case PROP_PROFILE: - sysprof_memprof_page_set_profile (self, g_value_get_object (value)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_memprof_page_class_init (SysprofMemprofPageClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); - SysprofPageClass *page_class = SYSPROF_PAGE_CLASS (klass); - - object_class->finalize = sysprof_memprof_page_finalize; - object_class->get_property = sysprof_memprof_page_get_property; - object_class->set_property = sysprof_memprof_page_set_property; - - page_class->load_async = sysprof_memprof_page_load_async; - page_class->load_finish = sysprof_memprof_page_load_finish; - - klass->go_previous = sysprof_memprof_page_real_go_previous; - - properties [PROP_PROFILE] = - g_param_spec_object ("profile", - "Profile", - "The callgraph profile to view", - SYSPROF_TYPE_MEMPROF_PROFILE, - (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_properties (object_class, N_PROPS, properties); - - signals [GO_PREVIOUS] = - g_signal_new ("go-previous", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_STRUCT_OFFSET (SysprofMemprofPageClass, go_previous), - NULL, NULL, NULL, G_TYPE_NONE, 0); - - gtk_widget_class_set_template_from_resource (widget_class, - "/org/gnome/sysprof/ui/sysprof-memprof-page.ui"); - - gtk_widget_class_bind_template_child_private (widget_class, SysprofMemprofPage, by_size); - gtk_widget_class_bind_template_child_private (widget_class, SysprofMemprofPage, callers_view); - gtk_widget_class_bind_template_child_private (widget_class, SysprofMemprofPage, function_size_cell); - gtk_widget_class_bind_template_child_private (widget_class, SysprofMemprofPage, function_size_column); - gtk_widget_class_bind_template_child_private (widget_class, SysprofMemprofPage, functions_view); - gtk_widget_class_bind_template_child_private (widget_class, SysprofMemprofPage, descendants_view); - gtk_widget_class_bind_template_child_private (widget_class, SysprofMemprofPage, descendants_name_column); - gtk_widget_class_bind_template_child_private (widget_class, SysprofMemprofPage, stack); - gtk_widget_class_bind_template_child_private (widget_class, SysprofMemprofPage, all_allocs); - gtk_widget_class_bind_template_child_private (widget_class, SysprofMemprofPage, temp_allocs); - gtk_widget_class_bind_template_child_private (widget_class, SysprofMemprofPage, summary); - gtk_widget_class_bind_template_child_private (widget_class, SysprofMemprofPage, temp_allocs_count); - gtk_widget_class_bind_template_child_private (widget_class, SysprofMemprofPage, num_allocs); - gtk_widget_class_bind_template_child_private (widget_class, SysprofMemprofPage, leaked_allocs); - gtk_widget_class_bind_template_child_private (widget_class, SysprofMemprofPage, leaked_allocs_button); - gtk_widget_class_bind_template_child_private (widget_class, SysprofMemprofPage, loading_state); - gtk_widget_class_bind_template_child_private (widget_class, SysprofMemprofPage, empty_state); - gtk_widget_class_bind_template_child_private (widget_class, SysprofMemprofPage, summary_page); - gtk_widget_class_bind_template_child_private (widget_class, SysprofMemprofPage, callgraph); - - gtk_widget_class_install_action (widget_class, "page.copy", NULL, sysprof_memprof_page_copy_cb); - - gtk_widget_class_add_binding_action (widget_class, GDK_KEY_c, GDK_CONTROL_MASK, "page.copy", NULL); - gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_Left, GDK_ALT_MASK, "go-previous", NULL); - - g_type_ensure (EGG_TYPE_PANED); - g_type_ensure (SYSPROF_TYPE_CELL_RENDERER_PERCENT); -} - -static void -sysprof_memprof_page_init (SysprofMemprofPage *self) -{ - SysprofMemprofPagePrivate *priv = sysprof_memprof_page_get_instance_private (self); - GtkTreeSelection *selection; - GtkCellRenderer *cell; - - priv->history = g_queue_new (); - priv->mode = SYSPROF_MEMPROF_MODE_ALL_ALLOCS; - - gtk_widget_init_template (GTK_WIDGET (self)); - - gtk_stack_set_visible_child (priv->stack, priv->empty_state); - - gtk_list_box_set_header_func (priv->by_size, sep_header_func, NULL, NULL); - - g_signal_connect_object (priv->all_allocs, - "notify::active", - G_CALLBACK (mode_notify_active), - self, - G_CONNECT_SWAPPED); - g_signal_connect_object (priv->temp_allocs, - "notify::active", - G_CALLBACK (mode_notify_active), - self, - G_CONNECT_SWAPPED); - g_signal_connect_object (priv->leaked_allocs_button, - "notify::active", - G_CALLBACK (mode_notify_active), - self, - G_CONNECT_SWAPPED); - g_signal_connect_object (priv->summary, - "notify::active", - G_CALLBACK (mode_notify_active), - self, - G_CONNECT_SWAPPED); - - selection = gtk_tree_view_get_selection (priv->functions_view); - - g_signal_connect_object (selection, - "changed", - G_CALLBACK (sysprof_memprof_page_function_selection_changed), - self, - G_CONNECT_SWAPPED); - - g_signal_connect_object (priv->descendants_view, - "row-activated", - G_CALLBACK (sysprof_memprof_page_descendant_activated), - self, - G_CONNECT_SWAPPED); - - g_signal_connect_object (priv->callers_view, - "row-activated", - G_CALLBACK (sysprof_memprof_page_caller_activated), - self, - G_CONNECT_SWAPPED); - - g_signal_connect (priv->descendants_view, - "move-cursor", - G_CALLBACK (descendants_view_move_cursor_cb), - NULL); - - cell = g_object_new (GTK_TYPE_CELL_RENDERER_TEXT, - "ellipsize", PANGO_ELLIPSIZE_MIDDLE, - "xalign", 0.0f, - NULL); - gtk_tree_view_column_pack_start (priv->descendants_name_column, cell, TRUE); - gtk_tree_view_column_add_attribute (priv->descendants_name_column, cell, "text", 0); - - cell = g_object_new (GTK_TYPE_CELL_RENDERER_TEXT, - "foreground", "#666666", - "scale", PANGO_SCALE_SMALL, - "xalign", 1.0f, - NULL); - gtk_tree_view_column_pack_start (priv->descendants_name_column, cell, FALSE); - gtk_tree_view_column_set_cell_data_func (priv->descendants_name_column, cell, - sysprof_memprof_page_tag_data_func, - self, NULL); - - gtk_tree_view_column_set_cell_data_func (priv->function_size_column, - GTK_CELL_RENDERER (priv->function_size_cell), - sysprof_memprof_page_size_data_func, - self, NULL); - - gtk_tree_selection_set_mode (gtk_tree_view_get_selection (priv->descendants_view), - GTK_SELECTION_MULTIPLE); -} - -typedef struct _Descendant Descendant; - -struct _Descendant -{ - const gchar *name; - guint self; - guint cumulative; - Descendant *parent; - Descendant *siblings; - Descendant *children; -}; - -static void -build_tree_cb (StackLink *trace, - gint size, - gpointer user_data) -{ - Descendant **tree = user_data; - Descendant *parent = NULL; - StackLink *link; - - g_assert (trace != NULL); - g_assert (tree != NULL); - - /* Get last item */ - link = trace; - while (link->next) - link = link->next; - - for (; link != NULL; link = link->prev) - { - const gchar *address = U64_TO_POINTER (link->data); - Descendant *prev = NULL; - Descendant *match = NULL; - - for (match = *tree; match != NULL; match = match->siblings) - { - if (match->name == address) - { - if (prev != NULL) - { - /* Move to front */ - prev->siblings = match->siblings; - match->siblings = *tree; - *tree = match; - } - break; - } - } - - if (match == NULL) - { - /* Have we seen this object further up the tree? */ - for (match = parent; match != NULL; match = match->parent) - { - if (match->name == address) - break; - } - } - - if (match == NULL) - { - match = g_slice_new (Descendant); - match->name = address; - match->cumulative = 0; - match->self = 0; - match->children = NULL; - match->parent = parent; - match->siblings = *tree; - *tree = match; - } - - tree = &match->children; - parent = match; - } - - parent->self += size; - - for (; parent != NULL; parent = parent->parent) - parent->cumulative += size; -} - -static Descendant * -build_tree (StackNode *node) -{ - Descendant *tree = NULL; - - for (; node != NULL; node = node->next) - { - if (node->toplevel) - stack_node_foreach_trace (node, build_tree_cb, &tree); - } - - return tree; -} - -static void -append_to_tree_and_free (SysprofMemprofPage *self, - StackStash *stash, - GtkTreeStore *store, - Descendant *item, - GtkTreeIter *parent) -{ - StackNode *node = NULL; - GtkTreeIter iter; - guint profile_size; - - g_assert (GTK_IS_TREE_STORE (store)); - g_assert (item != NULL); - - profile_size = MAX (1, sysprof_memprof_page_get_profile_size (self)); - - gtk_tree_store_append (store, &iter, parent); - - node = stack_stash_find_node (stash, (gpointer)item->name); - - gtk_tree_store_set (store, &iter, - COLUMN_NAME, item->name, - COLUMN_SELF, item->self * 100.0 / (gdouble)profile_size, - COLUMN_TOTAL, item->cumulative * 100.0 / (gdouble)profile_size, - COLUMN_POINTER, node, - COLUMN_SIZE, item->cumulative, - -1); - - if (item->siblings != NULL) - append_to_tree_and_free (self, stash, store, item->siblings, parent); - - if (item->children != NULL) - append_to_tree_and_free (self, stash, store, item->children, &iter); - - g_slice_free (Descendant, item); -} - -static void -sysprof_memprof_page_update_descendants (SysprofMemprofPage *self, - StackNode *node) -{ - SysprofMemprofPagePrivate *priv = sysprof_memprof_page_get_instance_private (self); - GtkTreeStore *store; - - g_assert (SYSPROF_IS_MEMPROF_PAGE (self)); - - if (g_queue_peek_head (priv->history) != node) - g_queue_push_head (priv->history, node); - - store = gtk_tree_store_new (5, - G_TYPE_STRING, - G_TYPE_DOUBLE, - G_TYPE_DOUBLE, - G_TYPE_POINTER, - G_TYPE_UINT64); - - if (priv->profile != NULL) - { - StackStash *stash; - - stash = sysprof_memprof_profile_get_stash (priv->profile); - if (stash != NULL) - { - Descendant *tree; - - tree = build_tree (node); - if (tree != NULL) - append_to_tree_and_free (self, stash, store, tree, NULL); - } - } - - gtk_tree_view_set_model (priv->descendants_view, GTK_TREE_MODEL (store)); - gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store), - COLUMN_TOTAL, GTK_SORT_DESCENDING); - sysprof_memprof_page_expand_descendants (self); - - g_clear_object (&store); -} - -/** - * sysprof_memprof_page_screenshot: - * @self: A #SysprofMemprofPage. - * - * This function will generate a text representation of the descendants tree. - * This is useful if you want to include various profiling information in a - * commit message or email. - * - * The text generated will match the current row expansion in the tree view. - * - * Returns: (nullable) (transfer full): A newly allocated string that should be freed - * with g_free(). - */ -gchar * -sysprof_memprof_page_screenshot (SysprofMemprofPage *self) -{ - SysprofMemprofPagePrivate *priv = sysprof_memprof_page_get_instance_private (self); - GtkTreeView *tree_view; - GtkTreeModel *model; - GtkTreePath *tree_path; - GString *str; - GtkTreeIter iter; - - g_return_val_if_fail (SYSPROF_IS_MEMPROF_PAGE (self), NULL); - - tree_view = priv->descendants_view; - - if (NULL == (model = gtk_tree_view_get_model (tree_view))) - return NULL; - - /* - * To avoid having to precalculate the deepest visible row, we - * put the timing information at the beginning of the line. - */ - - str = g_string_new (" SELF CUMULATIVE FUNCTION\n"); - tree_path = gtk_tree_path_new_first (); - - for (;;) - { - if (gtk_tree_model_get_iter (model, &iter, tree_path)) - { - guint depth = gtk_tree_path_get_depth (tree_path); - StackNode *node; - gdouble in_self; - gdouble total; - guint i; - - gtk_tree_model_get (model, &iter, - COLUMN_SELF, &in_self, - COLUMN_TOTAL, &total, - COLUMN_POINTER, &node, - -1); - - g_string_append_printf (str, "[% 7.2lf%%] [% 7.2lf%%] ", in_self, total); - - for (i = 0; i < depth; i++) - g_string_append (str, " "); - g_string_append (str, GSIZE_TO_POINTER (node->data)); - g_string_append_c (str, '\n'); - - if (gtk_tree_view_row_expanded (tree_view, tree_path)) - gtk_tree_path_down (tree_path); - else - gtk_tree_path_next (tree_path); - - continue; - } - - if (!gtk_tree_path_up (tree_path) || !gtk_tree_path_get_depth (tree_path)) - break; - - gtk_tree_path_next (tree_path); - } - - gtk_tree_path_free (tree_path); - - return g_string_free (str, FALSE); -} - -guint -sysprof_memprof_page_get_n_functions (SysprofMemprofPage *self) -{ - SysprofMemprofPagePrivate *priv = sysprof_memprof_page_get_instance_private (self); - GtkTreeModel *model; - guint ret = 0; - - g_return_val_if_fail (SYSPROF_IS_MEMPROF_PAGE (self), 0); - - if (NULL != (model = gtk_tree_view_get_model (priv->functions_view))) - ret = gtk_tree_model_iter_n_children (model, NULL); - - return ret; -} - -void -_sysprof_memprof_page_set_loading (SysprofMemprofPage *self, - gboolean loading) -{ - SysprofMemprofPagePrivate *priv = sysprof_memprof_page_get_instance_private (self); - - g_return_if_fail (SYSPROF_IS_MEMPROF_PAGE (self)); - - if (loading) - priv->loading++; - else - priv->loading--; - - if (priv->loading) - gtk_stack_set_visible_child (priv->stack, priv->loading_state); - else - gtk_stack_set_visible_child (priv->stack, priv->callgraph); -} diff --git a/src/libsysprof-ui/sysprof-memprof-page.h b/src/libsysprof-ui/sysprof-memprof-page.h deleted file mode 100644 index 1b3f7af3..00000000 --- a/src/libsysprof-ui/sysprof-memprof-page.h +++ /dev/null @@ -1,51 +0,0 @@ -/* sysprof-memprof-page.h - * - * Copyright 2016-2019 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 - -#include "sysprof-page.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_MEMPROF_PAGE (sysprof_memprof_page_get_type()) - -G_DECLARE_DERIVABLE_TYPE (SysprofMemprofPage, sysprof_memprof_page, SYSPROF, MEMPROF_PAGE, SysprofPage) - -struct _SysprofMemprofPageClass -{ - SysprofPageClass parent_class; - - void (*go_previous) (SysprofMemprofPage *self); - - /*< private >*/ - gpointer _reserved[16]; -}; - -GtkWidget *sysprof_memprof_page_new (void); -SysprofMemprofProfile *sysprof_memprof_page_get_profile (SysprofMemprofPage *self); -void sysprof_memprof_page_set_profile (SysprofMemprofPage *self, - SysprofMemprofProfile *profile); -gchar *sysprof_memprof_page_screenshot (SysprofMemprofPage *self); -guint sysprof_memprof_page_get_n_functions (SysprofMemprofPage *self); - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-memprof-page.ui b/src/libsysprof-ui/sysprof-memprof-page.ui deleted file mode 100644 index ca22240a..00000000 --- a/src/libsysprof-ui/sysprof-memprof-page.ui +++ /dev/null @@ -1,330 +0,0 @@ - - - - diff --git a/src/libsysprof-ui/sysprof-memprof-visualizer.c b/src/libsysprof-ui/sysprof-memprof-visualizer.c deleted file mode 100644 index a117dd7f..00000000 --- a/src/libsysprof-ui/sysprof-memprof-visualizer.c +++ /dev/null @@ -1,613 +0,0 @@ -/* sysprof-memprof-visualizer.c - * - * Copyright 2020 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" - -#define G_LOG_DOMAIN "sysprof-memprof-visualizer" - -#include -#include -#include - -#include "rax.h" - -#include "sysprof-memprof-visualizer.h" - -typedef struct -{ - cairo_surface_t *surface; - SysprofCaptureReader *reader; - rax *rax; - GtkAllocation alloc; - gint64 begin_time; - gint64 duration; - gint64 total_alloc; - gint64 max_alloc; - GdkRGBA fg; - GdkRGBA fg2; - guint scale; -} DrawContext; - -struct _SysprofMemprofVisualizer -{ - SysprofVisualizer parent_instance; - - SysprofCaptureReader *reader; - GCancellable *cancellable; - - cairo_surface_t *surface; - gint surface_w; - gint surface_h; - - guint queued_draw; - - gint64 begin_time; - gint64 duration; - - gint64 cached_total_alloc; - gint64 cached_max_alloc; - - guint mode : 1; -}; - -enum { - MODE_ALLOCS, - MODE_TOTAL, -}; - -G_DEFINE_TYPE (SysprofMemprofVisualizer, sysprof_memprof_visualizer, SYSPROF_TYPE_VISUALIZER) - -static void -draw_context_free (DrawContext *draw) -{ - g_clear_pointer (&draw->reader, sysprof_capture_reader_unref); - g_clear_pointer (&draw->surface, cairo_surface_destroy); - g_clear_pointer (&draw->rax, raxFree); - g_slice_free (DrawContext, draw); -} - -static void -sysprof_memprof_visualizer_set_reader (SysprofVisualizer *visualizer, - SysprofCaptureReader *reader) -{ - SysprofMemprofVisualizer *self = (SysprofMemprofVisualizer *)visualizer; - - g_assert (SYSPROF_IS_MEMPROF_VISUALIZER (self)); - - if (reader == self->reader) - return; - - g_clear_pointer (&self->reader, sysprof_capture_reader_unref); - - self->reader = sysprof_capture_reader_ref (reader); - self->begin_time = sysprof_capture_reader_get_start_time (reader); - self->duration = sysprof_capture_reader_get_end_time (reader) - - sysprof_capture_reader_get_start_time (reader); - - gtk_widget_queue_draw (GTK_WIDGET (self)); -} - -SysprofVisualizer * -sysprof_memprof_visualizer_new (gboolean total_allocs) -{ - SysprofMemprofVisualizer *self; - - self = g_object_new (SYSPROF_TYPE_MEMPROF_VISUALIZER, - "title", total_allocs ? _("Memory Used") : _("Memory Allocations"), - "height-request", 35, - "visible", TRUE, - NULL); - - if (total_allocs) - self->mode = MODE_TOTAL; - else - self->mode = MODE_ALLOCS; - - return SYSPROF_VISUALIZER (self); -} - -static guint64 -get_max_alloc (SysprofCaptureReader *reader) -{ - SysprofCaptureFrameType type; - gint64 ret = 0; - - while (sysprof_capture_reader_peek_type (reader, &type)) - { - const SysprofCaptureAllocation *ev; - - if (type == SYSPROF_CAPTURE_FRAME_ALLOCATION) - { - if (!(ev = sysprof_capture_reader_read_allocation (reader))) - break; - - if (ev->alloc_size > ret) - ret = ev->alloc_size; - } - else - { - if (!sysprof_capture_reader_skip (reader)) - break; - continue; - } - } - - sysprof_capture_reader_reset (reader); - - return ret; -} - -static guint64 -get_total_alloc (SysprofCaptureReader *reader) -{ - SysprofCaptureFrameType type; - guint64 total = 0; - guint64 max = 0; - rax *r; - - r = raxNew (); - - while (sysprof_capture_reader_peek_type (reader, &type)) - { - const SysprofCaptureAllocation *ev; - - if (type == SYSPROF_CAPTURE_FRAME_ALLOCATION) - { - if (!(ev = sysprof_capture_reader_read_allocation (reader))) - break; - - if (ev->alloc_size > 0) - { - raxInsert (r, - (guint8 *)&ev->alloc_addr, - sizeof ev->alloc_addr, - GSIZE_TO_POINTER (ev->alloc_size), - NULL); - - total += ev->alloc_size; - - if (total > max) - max = total; - } - else - { - gpointer res = raxFind (r, (guint8 *)&ev->alloc_addr, sizeof ev->alloc_addr); - - if (res != raxNotFound) - { - total -= GPOINTER_TO_SIZE (res); - raxRemove (r, - (guint8 *)&ev->alloc_addr, - sizeof ev->alloc_addr, - NULL); - } - } - } - else - { - if (!sysprof_capture_reader_skip (reader)) - break; - continue; - } - } - - sysprof_capture_reader_reset (reader); - raxFree (r); - - return max; -} - -static void -draw_total_worker (GTask *task, - gpointer source_object, - gpointer task_data, - GCancellable *cancellable) -{ - SysprofCaptureFrameType type; - DrawContext *draw = task_data; - gint64 total = 0; - cairo_t *cr; - rax *r; - gint x = 0; - - g_assert (G_IS_TASK (task)); - g_assert (draw != NULL); - g_assert (draw->surface != NULL); - g_assert (draw->reader != NULL); - g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); - - if (draw->total_alloc == 0) - draw->total_alloc = get_total_alloc (draw->reader); - - r = raxNew (); - - /* To avoid sorting, this code assums that all allocation information - * is sorted and in order. Generally this is the case, but a crafted - * syscap file could break it on purpose if they tried. - */ - - cr = cairo_create (draw->surface); - cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE); - cairo_set_source_rgb (cr, 0, 0, 0); - - while (sysprof_capture_reader_peek_type (draw->reader, &type)) - { - const SysprofCaptureAllocation *ev; - gint y; - - if (type == SYSPROF_CAPTURE_FRAME_ALLOCATION) - { - if (!(ev = sysprof_capture_reader_read_allocation (draw->reader))) - break; - - if (ev->alloc_size > 0) - { - raxInsert (r, - (guint8 *)&ev->alloc_addr, - sizeof ev->alloc_addr, - GSIZE_TO_POINTER (ev->alloc_size), - NULL); - - total += ev->alloc_size; - } - else - { - gpointer res = raxFind (r, (guint8 *)&ev->alloc_addr, sizeof ev->alloc_addr); - - if (res != raxNotFound) - { - total -= GPOINTER_TO_SIZE (res); - raxRemove (r, - (guint8 *)&ev->alloc_addr, - sizeof ev->alloc_addr, - NULL); - } - } - } - else - { - if (!sysprof_capture_reader_skip (draw->reader)) - break; - continue; - } - - x = (ev->frame.time - draw->begin_time) / (gdouble)draw->duration * draw->alloc.width; - y = draw->alloc.height - ((gdouble)total / (gdouble)draw->total_alloc * (gdouble)draw->alloc.height); - - cairo_rectangle (cr, x, y, 1, 1); - cairo_fill (cr); - } - - cairo_destroy (cr); - - g_task_return_boolean (task, TRUE); - - raxFree (r); -} - -static void -draw_alloc_worker (GTask *task, - gpointer source_object, - gpointer task_data, - GCancellable *cancellable) -{ - static const gdouble dashes[] = { 1.0, 2.0 }; - DrawContext *draw = task_data; - SysprofCaptureFrameType type; - GdkRGBA *last; - GdkRGBA mid; - cairo_t *cr; - guint counter = 0; - gint midpt; - gdouble log_max; - - g_assert (G_IS_TASK (task)); - g_assert (draw != NULL); - g_assert (draw->surface != NULL); - g_assert (draw->reader != NULL); - g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); - - if (draw->max_alloc == 0) - draw->max_alloc = get_max_alloc (draw->reader); - - log_max = log10 (draw->max_alloc); - midpt = draw->alloc.height / 2; - - cr = cairo_create (draw->surface); - - /* Draw mid-point line */ - mid = draw->fg; - mid.alpha *= 0.4; - cairo_set_line_width (cr, 1.0); - gdk_cairo_set_source_rgba (cr, &mid); - cairo_move_to (cr, 0, midpt); - cairo_line_to (cr, draw->alloc.width, midpt); - cairo_set_dash (cr, dashes, G_N_ELEMENTS (dashes), 0); - cairo_stroke (cr); - - cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE); - gdk_cairo_set_source_rgba (cr, &draw->fg); - last = &draw->fg; - - /* Now draw data points */ - while (sysprof_capture_reader_peek_type (draw->reader, &type)) - { - const SysprofCaptureAllocation *ev; - gint64 size; - gdouble l; - gint x; - gint y; - - /* Cancellation check every 1000 frames */ - if G_UNLIKELY (++counter == 1000) - { - if (g_task_return_error_if_cancelled (task)) - { - cairo_destroy (cr); - return; - } - - counter = 0; - } - - /* We only care about memory frames here */ - if (type != SYSPROF_CAPTURE_FRAME_ALLOCATION) - { - if (!sysprof_capture_reader_skip (draw->reader)) - break; - continue; - } - - if (!(ev = sysprof_capture_reader_read_allocation (draw->reader))) - break; - - if (ev->alloc_size > 0) - { - size = ev->alloc_size; - raxInsert (draw->rax, (guint8 *)&ev->alloc_addr, sizeof ev->alloc_addr, GSIZE_TO_POINTER (size), NULL); - - if (last != &draw->fg) - { - gdk_cairo_set_source_rgba (cr, &draw->fg); - last = &draw->fg; - } - } - else - { - size = GPOINTER_TO_SIZE (raxFind (draw->rax, (guint8 *)&ev->alloc_addr, sizeof ev->alloc_addr)); - if (size) - raxRemove (draw->rax, (guint8 *)&ev->alloc_addr, sizeof ev->alloc_addr, NULL); - - if (last != &draw->fg2) - { - gdk_cairo_set_source_rgba (cr, &draw->fg2); - last = &draw->fg2; - } - } - - l = log10 (size); - - x = (ev->frame.time - draw->begin_time) / (gdouble)draw->duration * draw->alloc.width; - - if (ev->alloc_size > 0) - y = midpt - ((l / log_max) * midpt); - else - y = midpt + ((l / log_max) * midpt); - - /* Fill immediately instead of batching draws so that - * we don't take a lot of memory to hold on to the - * path while drawing. - */ - cairo_rectangle (cr, x, y, 1, 1); - cairo_fill (cr); - } - - cairo_destroy (cr); - - g_task_return_boolean (task, TRUE); -} - -static void -draw_finished (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - g_autoptr(SysprofMemprofVisualizer) self = user_data; - g_autoptr(GError) error = NULL; - - g_assert (object == NULL); - g_assert (G_IS_TASK (result)); - g_assert (SYSPROF_IS_MEMPROF_VISUALIZER (self)); - - if (g_task_propagate_boolean (G_TASK (result), &error)) - { - DrawContext *draw = g_task_get_task_data (G_TASK (result)); - - g_clear_pointer (&self->surface, cairo_surface_destroy); - - self->surface = g_steal_pointer (&draw->surface); - self->surface_w = draw->alloc.width; - self->surface_h = draw->alloc.height; - self->cached_max_alloc = draw->max_alloc; - self->cached_total_alloc = draw->total_alloc; - - gtk_widget_queue_draw (GTK_WIDGET (self)); - } -} - -static gboolean -sysprof_memprof_visualizer_begin_draw (SysprofMemprofVisualizer *self) -{ - g_autoptr(GTask) task = NULL; - GtkAllocation alloc; - DrawContext *draw; - - g_assert (SYSPROF_IS_MEMPROF_VISUALIZER (self)); - - self->queued_draw = 0; - - /* Make sure we even need to draw */ - gtk_widget_get_allocation (GTK_WIDGET (self), &alloc); - if (self->reader == NULL || - !gtk_widget_get_visible (GTK_WIDGET (self)) || - !gtk_widget_get_mapped (GTK_WIDGET (self)) || - alloc.width == 0 || alloc.height == 0) - return G_SOURCE_REMOVE; - - /* Some GPUs (Intel) cannot deal with graphics textures larger than - * 8000x8000. So here we are going to cheat a bit and just use that as our - * max, and scale when drawing. The biggest issue here is that long term we - * need a tiling solution that lets us render lots of tiles and then draw - * them as necessary. - */ - if (alloc.width > 8000) - alloc.width = 8000; - - draw = g_slice_new0 (DrawContext); - draw->rax = raxNew (); - draw->alloc.width = alloc.width; - draw->alloc.height = alloc.height; - draw->reader = sysprof_capture_reader_copy (self->reader); - draw->begin_time = self->begin_time; - draw->duration = self->duration; - draw->scale = gtk_widget_get_scale_factor (GTK_WIDGET (self)); - draw->max_alloc = self->cached_max_alloc; - draw->total_alloc = self->cached_total_alloc; - - gdk_rgba_parse (&draw->fg, "rgba(246,97,81,1)"); - gdk_rgba_parse (&draw->fg2, "rgba(245,194,17,1)"); - - draw->surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, - alloc.width * draw->scale, - alloc.height * draw->scale); - cairo_surface_set_device_scale (draw->surface, draw->scale, draw->scale); - - g_cancellable_cancel (self->cancellable); - g_clear_object (&self->cancellable); - self->cancellable = g_cancellable_new (); - - task = g_task_new (NULL, self->cancellable, draw_finished, g_object_ref (self)); - g_task_set_source_tag (task, sysprof_memprof_visualizer_begin_draw); - g_task_set_task_data (task, g_steal_pointer (&draw), (GDestroyNotify)draw_context_free); - - if (self->mode == MODE_ALLOCS) - g_task_run_in_thread (task, draw_alloc_worker); - else - g_task_run_in_thread (task, draw_total_worker); - - return G_SOURCE_REMOVE; -} - -static void -sysprof_memprof_visualizer_queue_redraw (SysprofMemprofVisualizer *self) -{ - g_assert (SYSPROF_IS_MEMPROF_VISUALIZER (self)); - - if (self->queued_draw == 0) - self->queued_draw = g_idle_add_full (G_PRIORITY_HIGH_IDLE, - (GSourceFunc) sysprof_memprof_visualizer_begin_draw, - g_object_ref (self), - g_object_unref); -} - -static void -sysprof_memprof_visualizer_size_allocate (GtkWidget *widget, - int width, - int height, - int baseline) -{ - sysprof_memprof_visualizer_queue_redraw (SYSPROF_MEMPROF_VISUALIZER (widget)); -} - -static void -sysprof_memprof_visualizer_dispose (GObject *object) -{ - SysprofMemprofVisualizer *self = (SysprofMemprofVisualizer *)object; - - g_clear_pointer (&self->reader, sysprof_capture_reader_unref); - g_clear_pointer (&self->surface, cairo_surface_destroy); - g_clear_handle_id (&self->queued_draw, g_source_remove); - - G_OBJECT_CLASS (sysprof_memprof_visualizer_parent_class)->dispose (object); -} - -static void -sysprof_memprof_visualizer_snapshot (GtkWidget *widget, - GtkSnapshot *snapshot) -{ - SysprofMemprofVisualizer *self = (SysprofMemprofVisualizer *)widget; - - g_assert (SYSPROF_IS_MEMPROF_VISUALIZER (self)); - g_assert (GTK_IS_SNAPSHOT (snapshot)); - - GTK_WIDGET_CLASS (sysprof_memprof_visualizer_parent_class)->snapshot (widget, snapshot); - - if (self->surface != NULL) - { - cairo_t *cr; - GtkAllocation alloc; - - gtk_widget_get_allocation (widget, &alloc); - - cr = gtk_snapshot_append_cairo (snapshot, &GRAPHENE_RECT_INIT (0, 0, alloc.width, alloc.height)); - - cairo_save (cr); - cairo_rectangle (cr, 0, 0, alloc.width, alloc.height); - - /* We might be drawing an updated image in the background, and this - * will take our current surface (which is the wrong size) and draw - * it stretched to fit the allocation. That gives us *something* that - * represents the end result even if it is a bit blurry in the mean - * time. Allocators take a while to render anyway. - */ - if (self->surface_w != alloc.width || self->surface_h != alloc.height) - { - cairo_scale (cr, - (gdouble)alloc.width / (gdouble)self->surface_w, - (gdouble)alloc.height / (gdouble)self->surface_h); - } - - cairo_set_source_surface (cr, self->surface, 0, 0); - cairo_paint (cr); - cairo_restore (cr); - - cairo_destroy (cr); - } -} - -static void -sysprof_memprof_visualizer_class_init (SysprofMemprofVisualizerClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); - SysprofVisualizerClass *visualizer_class = SYSPROF_VISUALIZER_CLASS (klass); - - object_class->dispose = sysprof_memprof_visualizer_dispose; - - widget_class->snapshot = sysprof_memprof_visualizer_snapshot; - widget_class->size_allocate = sysprof_memprof_visualizer_size_allocate; - - visualizer_class->set_reader = sysprof_memprof_visualizer_set_reader; -} - -static void -sysprof_memprof_visualizer_init (SysprofMemprofVisualizer *self) -{ -} diff --git a/src/libsysprof-ui/sysprof-memprof-visualizer.h b/src/libsysprof-ui/sysprof-memprof-visualizer.h deleted file mode 100644 index 04f078c8..00000000 --- a/src/libsysprof-ui/sysprof-memprof-visualizer.h +++ /dev/null @@ -1,33 +0,0 @@ -/* sysprof-memprof-visualizer.h - * - * Copyright 2020 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-visualizer.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_MEMPROF_VISUALIZER (sysprof_memprof_visualizer_get_type()) - -G_DECLARE_FINAL_TYPE (SysprofMemprofVisualizer, sysprof_memprof_visualizer, SYSPROF, MEMPROF_VISUALIZER, SysprofVisualizer) - -SysprofVisualizer *sysprof_memprof_visualizer_new (gboolean total_allocs); - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-model-filter.c b/src/libsysprof-ui/sysprof-model-filter.c deleted file mode 100644 index 48c9386e..00000000 --- a/src/libsysprof-ui/sysprof-model-filter.c +++ /dev/null @@ -1,497 +0,0 @@ -/* sysprof-model-filter.c - * - * Copyright 2016-2019 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-model-filter.h" - -typedef struct -{ - GSequenceIter *child_iter; - GSequenceIter *filter_iter; -} SysprofModelFilterItem; - -typedef struct -{ - /* The list we are filtering */ - GListModel *child_model; - - /* - * Both sequences point to the same SysprofModelFilterItem which - * contains cross-referencing stable GSequenceIter pointers. - * The child_seq is considered the "owner" and used to release - * allocated resources. - */ - GSequence *child_seq; - GSequence *filter_seq; - - /* - * Typical set of callback/closure/free function pointers and data. - * Called for child items to determine visibility state. - */ - SysprofModelFilterFunc filter_func; - gpointer filter_func_data; - GDestroyNotify filter_func_data_destroy; - - /* - * If set, we will not emit items-changed. This is useful during - * invalidation so that we can do a single emission for all items - * that have changed. - */ - guint supress_items_changed : 1; -} SysprofModelFilterPrivate; - -static void list_model_iface_init (GListModelInterface *iface); - -G_DEFINE_TYPE_EXTENDED (SysprofModelFilter, sysprof_model_filter, G_TYPE_OBJECT, 0, - G_ADD_PRIVATE (SysprofModelFilter) - G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, - list_model_iface_init)) - -enum { - PROP_0, - PROP_CHILD_MODEL, - N_PROPS -}; - -static GParamSpec *properties [N_PROPS]; -static guint signal_id; - -static void -sysprof_model_filter_item_free (gpointer data) -{ - SysprofModelFilterItem *item = data; - - g_clear_pointer (&item->filter_iter, g_sequence_remove); - item->child_iter = NULL; - g_slice_free (SysprofModelFilterItem, item); -} - -static gboolean -sysprof_model_filter_default_filter_func (GObject *item, - gpointer user_data) -{ - return TRUE; -} - -/* - * Locates the next item in the filter sequence starting from - * the cross-reference found at @iter. If none are found, the - * end_iter for the filter sequence is returned. - * - * This returns an iter in the filter_sequence, not the child_seq. - * - * Returns: a #GSequenceIter from the filter sequence. - */ -static GSequenceIter * -find_next_visible_filter_iter (SysprofModelFilter *self, - GSequenceIter *iter) -{ - SysprofModelFilterPrivate *priv = sysprof_model_filter_get_instance_private (self); - - g_assert (SYSPROF_IS_MODEL_FILTER (self)); - g_assert (iter != NULL); - - for (; !g_sequence_iter_is_end (iter); iter = g_sequence_iter_next (iter)) - { - SysprofModelFilterItem *item = g_sequence_get (iter); - - g_assert (item->child_iter == iter); - g_assert (item->filter_iter == NULL || - g_sequence_iter_get_sequence (item->filter_iter) == priv->filter_seq); - - if (item->filter_iter != NULL) - return item->filter_iter; - } - - return g_sequence_get_end_iter (priv->filter_seq); -} - -static void -sysprof_model_filter_child_model_items_changed (SysprofModelFilter *self, - guint position, - guint n_removed, - guint n_added, - GListModel *child_model) -{ - SysprofModelFilterPrivate *priv = sysprof_model_filter_get_instance_private (self); - gboolean unblocked; - - g_assert (SYSPROF_IS_MODEL_FILTER (self)); - g_assert (G_IS_LIST_MODEL (child_model)); - g_assert (priv->child_model == child_model); - g_assert (position <= (guint)g_sequence_get_length (priv->child_seq)); - g_assert ((g_sequence_get_length (priv->child_seq) - n_removed + n_added) == - g_list_model_get_n_items (child_model)); - - unblocked = !priv->supress_items_changed; - - if (n_removed > 0) - { - GSequenceIter *iter = g_sequence_get_iter_at_pos (priv->child_seq, position); - gint first_position = -1; - guint count = 0; - - g_assert (!g_sequence_iter_is_end (iter)); - - /* Small shortcut when all items are removed */ - if (n_removed == (guint)g_sequence_get_length (priv->child_seq)) - { - g_sequence_remove_range (g_sequence_get_begin_iter (priv->child_seq), - g_sequence_get_end_iter (priv->child_seq)); - g_assert (g_sequence_is_empty (priv->child_seq)); - g_assert (g_sequence_is_empty (priv->filter_seq)); - goto add_new_items; - } - - for (guint i = 0; i < n_removed; i++) - { - GSequenceIter *to_remove = iter; - SysprofModelFilterItem *item = g_sequence_get (iter); - - g_assert (item != NULL); - g_assert (item->child_iter == iter); - g_assert (item->filter_iter == NULL || - g_sequence_iter_get_sequence (item->filter_iter) == priv->filter_seq); - - /* If this is visible, we need to notify about removal */ - if (unblocked && item->filter_iter != NULL) - { - if (first_position < 0) - first_position = g_sequence_iter_get_position (item->filter_iter); - - count++; - } - - /* Fetch the next while the iter is still valid */ - iter = g_sequence_iter_next (iter); - - /* Cascades into also removing from filter_seq. */ - g_sequence_remove (to_remove); - } - - if (unblocked && first_position >= 0) - g_list_model_items_changed (G_LIST_MODEL (self), first_position, count, 0); - } - -add_new_items: - - if (n_added > 0) - { - GSequenceIter *iter = g_sequence_get_iter_at_pos (priv->child_seq, position); - GSequenceIter *filter_iter = find_next_visible_filter_iter (self, iter); - guint filter_position = g_sequence_iter_get_position (filter_iter); - guint count = 0; - - /* Walk backwards to insert items into the filter list so that - * we can use the same filter_position for each items-changed - * signal emission. - */ - for (guint i = position + n_added; i > position; i--) - { - g_autoptr(GObject) instance = NULL; - SysprofModelFilterItem *item; - - item = g_slice_new0 (SysprofModelFilterItem); - item->filter_iter = NULL; - item->child_iter = g_sequence_insert_before (iter, item); - - instance = g_list_model_get_item (child_model, i - 1); - g_assert (G_IS_OBJECT (instance)); - - /* Check if this item is visible */ - if (priv->filter_func (instance, priv->filter_func_data)) - { - item->filter_iter = g_sequence_insert_before (filter_iter, item); - - /* Use this in the future for relative positioning */ - filter_iter = item->filter_iter; - - count++; - } - - /* Insert next item before this */ - iter = item->child_iter; - } - - if (unblocked && count) - g_list_model_items_changed (G_LIST_MODEL (self), filter_position, 0, count); - } - - g_assert ((guint)g_sequence_get_length (priv->child_seq) == - g_list_model_get_n_items (child_model)); -} - -static void -sysprof_model_filter_finalize (GObject *object) -{ - SysprofModelFilter *self = (SysprofModelFilter *)object; - SysprofModelFilterPrivate *priv = sysprof_model_filter_get_instance_private (self); - - g_clear_pointer (&priv->child_seq, g_sequence_free); - g_clear_pointer (&priv->filter_seq, g_sequence_free); - - if (priv->filter_func_data_destroy) - { - g_clear_pointer (&priv->filter_func_data, priv->filter_func_data_destroy); - priv->filter_func_data_destroy = NULL; - } - - g_clear_object (&priv->child_model); - - G_OBJECT_CLASS (sysprof_model_filter_parent_class)->finalize (object); -} - -static void -sysprof_model_filter_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - SysprofModelFilter *self = SYSPROF_MODEL_FILTER (object); - - switch (prop_id) - { - case PROP_CHILD_MODEL: - g_value_set_object (value, sysprof_model_filter_get_child_model (self)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_model_filter_class_init (SysprofModelFilterClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->finalize = sysprof_model_filter_finalize; - object_class->get_property = sysprof_model_filter_get_property; - - properties [PROP_CHILD_MODEL] = - g_param_spec_object ("child-model", - "Child Model", - "The child model being filtered.", - G_TYPE_LIST_MODEL, - (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_properties (object_class, N_PROPS, properties); - - signal_id = g_signal_lookup ("items-changed", SYSPROF_TYPE_MODEL_FILTER); -} - -static void -sysprof_model_filter_init (SysprofModelFilter *self) -{ - SysprofModelFilterPrivate *priv = sysprof_model_filter_get_instance_private (self); - - priv->filter_func = sysprof_model_filter_default_filter_func; - priv->child_seq = g_sequence_new (sysprof_model_filter_item_free); - priv->filter_seq = g_sequence_new (NULL); -} - -static GType -sysprof_model_filter_get_item_type (GListModel *model) -{ - SysprofModelFilter *self = (SysprofModelFilter *)model; - SysprofModelFilterPrivate *priv = sysprof_model_filter_get_instance_private (self); - - g_assert (SYSPROF_IS_MODEL_FILTER (self)); - - return g_list_model_get_item_type (priv->child_model); -} - -static guint -sysprof_model_filter_get_n_items (GListModel *model) -{ - SysprofModelFilter *self = (SysprofModelFilter *)model; - SysprofModelFilterPrivate *priv = sysprof_model_filter_get_instance_private (self); - - g_assert (SYSPROF_IS_MODEL_FILTER (self)); - g_assert (priv->filter_seq != NULL); - - return g_sequence_get_length (priv->filter_seq); -} - -static gpointer -sysprof_model_filter_get_item (GListModel *model, - guint position) -{ - SysprofModelFilter *self = (SysprofModelFilter *)model; - SysprofModelFilterPrivate *priv = sysprof_model_filter_get_instance_private (self); - SysprofModelFilterItem *item; - GSequenceIter *iter; - guint child_position; - - g_assert (SYSPROF_IS_MODEL_FILTER (self)); - g_assert (position < (guint)g_sequence_get_length (priv->filter_seq)); - - iter = g_sequence_get_iter_at_pos (priv->filter_seq, position); - g_assert (!g_sequence_iter_is_end (iter)); - - item = g_sequence_get (iter); - g_assert (item != NULL); - g_assert (item->filter_iter == iter); - g_assert (item->child_iter != NULL); - g_assert (g_sequence_iter_get_sequence (item->child_iter) == priv->child_seq); - - child_position = g_sequence_iter_get_position (item->child_iter); - - return g_list_model_get_item (priv->child_model, child_position); -} - -static void -list_model_iface_init (GListModelInterface *iface) -{ - iface->get_item_type = sysprof_model_filter_get_item_type; - iface->get_n_items = sysprof_model_filter_get_n_items; - iface->get_item = sysprof_model_filter_get_item; -} - -SysprofModelFilter * -sysprof_model_filter_new (GListModel *child_model) -{ - SysprofModelFilter *ret; - SysprofModelFilterPrivate *priv; - - g_return_val_if_fail (G_IS_LIST_MODEL (child_model), NULL); - - ret = g_object_new (SYSPROF_TYPE_MODEL_FILTER, NULL); - priv = sysprof_model_filter_get_instance_private (ret); - priv->child_model = g_object_ref (child_model); - - g_signal_connect_object (child_model, - "items-changed", - G_CALLBACK (sysprof_model_filter_child_model_items_changed), - ret, - G_CONNECT_SWAPPED); - - sysprof_model_filter_invalidate (ret); - - return ret; -} - -/** - * sysprof_model_filter_get_child_model: - * @self: A #SysprofModelFilter - * - * Gets the child model that is being filtered. - * - * Returns: (transfer none): A #GListModel. - */ -GListModel * -sysprof_model_filter_get_child_model (SysprofModelFilter *self) -{ - SysprofModelFilterPrivate *priv = sysprof_model_filter_get_instance_private (self); - - g_return_val_if_fail (SYSPROF_IS_MODEL_FILTER (self), NULL); - - return priv->child_model; -} - -void -sysprof_model_filter_invalidate (SysprofModelFilter *self) -{ - SysprofModelFilterPrivate *priv = sysprof_model_filter_get_instance_private (self); - guint n_items; - - g_return_if_fail (SYSPROF_IS_MODEL_FILTER (self)); - - /* We block emission while in invalidate so that we can use - * a single larger items-changed rather lots of small emissions. - */ - priv->supress_items_changed = TRUE; - - /* First determine how many items we need to synthesize as a removal */ - n_items = g_sequence_get_length (priv->filter_seq); - - /* - * If we have a child store, we want to rebuild our list of items - * from scratch, so just remove everything. - */ - if (!g_sequence_is_empty (priv->child_seq)) - g_sequence_remove_range (g_sequence_get_begin_iter (priv->child_seq), - g_sequence_get_end_iter (priv->child_seq)); - - g_assert (g_sequence_is_empty (priv->child_seq)); - g_assert (g_sequence_is_empty (priv->filter_seq)); - g_assert (!priv->child_model || G_IS_LIST_MODEL (priv->child_model)); - - /* - * Now add the new items by synthesizing the addition of all the - * itmes in the list. - */ - if (priv->child_model != NULL) - { - guint child_n_items; - - /* - * Now add all the items as one shot to our list so that - * we get populate our sequence and filter sequence. - */ - child_n_items = g_list_model_get_n_items (priv->child_model); - sysprof_model_filter_child_model_items_changed (self, 0, 0, child_n_items, priv->child_model); - - g_assert ((guint)g_sequence_get_length (priv->child_seq) == child_n_items); - g_assert ((guint)g_sequence_get_length (priv->filter_seq) <= child_n_items); - } - - priv->supress_items_changed = FALSE; - - /* Now that we've updated our sequences, notify of all the changes - * as a single series of updates to the consumers. - */ - if (n_items > 0 || !g_sequence_is_empty (priv->filter_seq)) - g_list_model_items_changed (G_LIST_MODEL (self), - 0, - n_items, - g_sequence_get_length (priv->filter_seq)); -} - -void -sysprof_model_filter_set_filter_func (SysprofModelFilter *self, - SysprofModelFilterFunc filter_func, - gpointer filter_func_data, - GDestroyNotify filter_func_data_destroy) -{ - SysprofModelFilterPrivate *priv = sysprof_model_filter_get_instance_private (self); - - g_return_if_fail (SYSPROF_IS_MODEL_FILTER (self)); - g_return_if_fail (filter_func || (!filter_func_data && !filter_func_data_destroy)); - - if (priv->filter_func_data_destroy != NULL) - g_clear_pointer (&priv->filter_func_data, priv->filter_func_data_destroy); - - if (filter_func != NULL) - { - priv->filter_func = filter_func; - priv->filter_func_data = filter_func_data; - priv->filter_func_data_destroy = filter_func_data_destroy; - } - else - { - priv->filter_func = sysprof_model_filter_default_filter_func; - priv->filter_func_data = NULL; - priv->filter_func_data_destroy = NULL; - } - - sysprof_model_filter_invalidate (self); -} diff --git a/src/libsysprof-ui/sysprof-model-filter.h b/src/libsysprof-ui/sysprof-model-filter.h deleted file mode 100644 index a1b97bd4..00000000 --- a/src/libsysprof-ui/sysprof-model-filter.h +++ /dev/null @@ -1,60 +0,0 @@ -/* sysprof-model-filter.h - * - * Copyright 2016-2019 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 - -#if !defined (SYSPROF_UI_INSIDE) && !defined (SYSPROF_UI_COMPILATION) -# error "Only can be included directly." -#endif - -#include - -#include "sysprof-version-macros.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_MODEL_FILTER (sysprof_model_filter_get_type()) - -typedef gboolean (*SysprofModelFilterFunc) (GObject *object, - gpointer user_data); - -SYSPROF_AVAILABLE_IN_ALL -G_DECLARE_DERIVABLE_TYPE (SysprofModelFilter, sysprof_model_filter, SYSPROF, MODEL_FILTER, GObject) - -struct _SysprofModelFilterClass -{ - GObjectClass parent_class; - - gpointer padding[8]; -}; - -SYSPROF_AVAILABLE_IN_ALL -SysprofModelFilter *sysprof_model_filter_new (GListModel *child_model); -SYSPROF_AVAILABLE_IN_ALL -GListModel *sysprof_model_filter_get_child_model (SysprofModelFilter *self); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_model_filter_invalidate (SysprofModelFilter *self); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_model_filter_set_filter_func (SysprofModelFilter *self, - SysprofModelFilterFunc filter_func, - gpointer filter_func_data, - GDestroyNotify filter_func_data_destroy); - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-netdev-aid.c b/src/libsysprof-ui/sysprof-netdev-aid.c deleted file mode 100644 index 5f1c9f2c..00000000 --- a/src/libsysprof-ui/sysprof-netdev-aid.c +++ /dev/null @@ -1,266 +0,0 @@ -/* sysprof-netdev-aid.c - * - * Copyright 2019 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 - */ - -#define G_LOG_DOMAIN "sysprof-netdev-aid" - -#include "config.h" - -#include - -#include "sysprof-color-cycle.h" -#include "sysprof-duplex-visualizer.h" -#include "sysprof-netdev-aid.h" - -struct _SysprofNetdevAid -{ - SysprofAid parent_instance; -}; - -typedef struct -{ - SysprofCaptureCursor *cursor; - SysprofDisplay *display; -} Present; - -G_DEFINE_TYPE (SysprofNetdevAid, sysprof_netdev_aid, SYSPROF_TYPE_AID) - -static void -present_free (gpointer data) -{ - Present *p = data; - - g_clear_pointer (&p->cursor, sysprof_capture_cursor_unref); - g_clear_object (&p->display); - g_slice_free (Present, p); -} - -/** - * sysprof_netdev_aid_new: - * - * Create a new #SysprofNetdevAid. - * - * Returns: (transfer full): a newly created #SysprofNetdevAid - * - * Since: 3.34 - */ -SysprofAid * -sysprof_netdev_aid_new (void) -{ - return g_object_new (SYSPROF_TYPE_NETDEV_AID, NULL); -} - -static void -sysprof_netdev_aid_prepare (SysprofAid *self, - SysprofProfiler *profiler) -{ - g_autoptr(SysprofSource) source = NULL; - - g_assert (SYSPROF_IS_NETDEV_AID (self)); - g_assert (SYSPROF_IS_PROFILER (profiler)); - - source = sysprof_netdev_source_new (); - sysprof_profiler_add_source (profiler, source); -} - -static bool -collect_netdev_counters (const SysprofCaptureFrame *frame, - gpointer user_data) -{ - SysprofCaptureCounterDefine *def = (SysprofCaptureCounterDefine *)frame; - GArray *counters = user_data; - - g_assert (frame != NULL); - g_assert (frame->type == SYSPROF_CAPTURE_FRAME_CTRDEF); - g_assert (counters != NULL); - - for (guint i = 0; i < def->n_counters; i++) - { - const SysprofCaptureCounter *counter = &def->counters[i]; - - if (strcmp (counter->category, "Network") == 0 && - (g_str_has_prefix (counter->name, "RX Bytes") || - g_str_has_prefix (counter->name, "TX Bytes"))) - g_array_append_vals (counters, counter, 1); - } - - return TRUE; -} - -static void -sysprof_netdev_aid_present_worker (GTask *task, - gpointer source_object, - gpointer task_data, - GCancellable *cancellable) -{ - Present *present = task_data; - g_autoptr(GArray) counters = NULL; - - g_assert (G_IS_TASK (task)); - g_assert (SYSPROF_IS_NETDEV_AID (source_object)); - g_assert (present != NULL); - g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); - - counters = g_array_new (FALSE, FALSE, sizeof (SysprofCaptureCounter)); - sysprof_capture_cursor_foreach (present->cursor, collect_netdev_counters, counters); - g_task_return_pointer (task, - g_steal_pointer (&counters), - (GDestroyNotify) g_array_unref); -} - -static guint -find_other_id (GArray *counters, - const gchar *rx) -{ - g_autofree gchar *other = NULL; - - g_assert (counters); - g_assert (rx != NULL); - - other = g_strdup (rx); - other[0] = 'T'; - - for (guint i = 0; i < counters->len; i++) - { - const SysprofCaptureCounter *c = &g_array_index (counters, SysprofCaptureCounter, i); - - if (g_str_equal (c->name, other)) - return c->id; - } - - return 0; -} - -static void -sysprof_netdev_aid_present_async (SysprofAid *aid, - SysprofCaptureReader *reader, - SysprofDisplay *display, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - static const SysprofCaptureFrameType types[] = { SYSPROF_CAPTURE_FRAME_CTRDEF }; - g_autoptr(SysprofCaptureCondition) condition = NULL; - g_autoptr(SysprofCaptureCursor) cursor = NULL; - g_autoptr(GTask) task = NULL; - Present present; - - g_assert (SYSPROF_IS_NETDEV_AID (aid)); - g_assert (reader != NULL); - g_assert (SYSPROF_IS_DISPLAY (display)); - g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); - - condition = sysprof_capture_condition_new_where_type_in (1, types); - cursor = sysprof_capture_cursor_new (reader); - sysprof_capture_cursor_add_condition (cursor, g_steal_pointer (&condition)); - - present.cursor = g_steal_pointer (&cursor); - present.display = g_object_ref (display); - - task = g_task_new (aid, cancellable, callback, user_data); - g_task_set_source_tag (task, sysprof_netdev_aid_present_async); - g_task_set_task_data (task, - g_slice_dup (Present, &present), - present_free); - g_task_run_in_thread (task, sysprof_netdev_aid_present_worker); -} - -static gboolean -sysprof_netdev_aid_present_finish (SysprofAid *aid, - GAsyncResult *result, - GError **error) -{ - g_autoptr(GArray) counters = NULL; - Present *present; - - g_assert (SYSPROF_IS_AID (aid)); - g_assert (G_IS_TASK (result)); - - present = g_task_get_task_data (G_TASK (result)); - - if ((counters = g_task_propagate_pointer (G_TASK (result), error))) - { - g_autoptr(SysprofColorCycle) cycle = sysprof_color_cycle_new (); - SysprofVisualizerGroup *group; - - group = g_object_new (SYSPROF_TYPE_VISUALIZER_GROUP, - "can-focus", TRUE, - "title", _("Network"), - "visible", TRUE, - NULL); - - for (guint i = 0; i < counters->len; i++) - { - const SysprofCaptureCounter *ctr = &g_array_index (counters, SysprofCaptureCounter, i); - - if (g_str_has_prefix (ctr->name, "RX Bytes")) - { - g_autofree gchar *title = NULL; - gboolean is_combined; - GtkWidget *row; - GdkRGBA rgba; - guint other_id; - - if (!(other_id = find_other_id (counters, ctr->name))) - continue; - - is_combined = g_str_equal (ctr->description, "Combined"); - - if (is_combined) - title = g_strdup ("Network Bytes (All)"); - else - title = g_strdup_printf ("Network Bytes%s", ctr->name + strlen ("RX Bytes")); - - row = g_object_new (SYSPROF_TYPE_DUPLEX_VISUALIZER, - "title", title, - "height-request", 35, - "visible", is_combined, - NULL); - sysprof_color_cycle_next (cycle, &rgba); - sysprof_duplex_visualizer_set_counters (SYSPROF_DUPLEX_VISUALIZER (row), ctr->id, other_id); - sysprof_duplex_visualizer_set_colors (SYSPROF_DUPLEX_VISUALIZER (row), &rgba, &rgba); - sysprof_visualizer_group_insert (group, SYSPROF_VISUALIZER (row), is_combined ? 0 : -1, !is_combined); - } - } - - if (counters->len > 0) - sysprof_display_add_group (present->display, group); - else - g_object_unref (g_object_ref_sink (group)); - } - - return counters != NULL; -} - -static void -sysprof_netdev_aid_class_init (SysprofNetdevAidClass *klass) -{ - SysprofAidClass *aid_class = SYSPROF_AID_CLASS (klass); - - aid_class->prepare = sysprof_netdev_aid_prepare; - aid_class->present_async = sysprof_netdev_aid_present_async; - aid_class->present_finish = sysprof_netdev_aid_present_finish; -} - -static void -sysprof_netdev_aid_init (SysprofNetdevAid *self) -{ - sysprof_aid_set_display_name (SYSPROF_AID (self), _("Network")); - sysprof_aid_set_icon_name (SYSPROF_AID (self), "preferences-system-network-symbolic"); -} diff --git a/src/libsysprof-ui/sysprof-netdev-aid.h b/src/libsysprof-ui/sysprof-netdev-aid.h deleted file mode 100644 index 6bff1765..00000000 --- a/src/libsysprof-ui/sysprof-netdev-aid.h +++ /dev/null @@ -1,33 +0,0 @@ -/* sysprof-netdev-aid.h - * - * Copyright 2019 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-aid.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_NETDEV_AID (sysprof_netdev_aid_get_type()) - -G_DECLARE_FINAL_TYPE (SysprofNetdevAid, sysprof_netdev_aid, SYSPROF, NETDEV_AID, SysprofAid) - -SysprofAid *sysprof_netdev_aid_new (void); - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-notebook.c b/src/libsysprof-ui/sysprof-notebook.c deleted file mode 100644 index 1a040c28..00000000 --- a/src/libsysprof-ui/sysprof-notebook.c +++ /dev/null @@ -1,561 +0,0 @@ -/* sysprof-notebook.c - * - * Copyright 2019 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 - */ - -#define G_LOG_DOMAIN "sysprof-notebook" - -#include "config.h" - -#include "sysprof-display.h" -#include "sysprof-notebook.h" -#include "sysprof-tab.h" -#include "sysprof-ui-private.h" - -typedef struct -{ - GtkNotebook *notebook; - guint always_show_tabs : 1; -} SysprofNotebookPrivate; - -static void buildable_iface_init (GtkBuildableIface *iface); - -G_DEFINE_TYPE_WITH_CODE (SysprofNotebook, sysprof_notebook, GTK_TYPE_WIDGET, - G_ADD_PRIVATE (SysprofNotebook) - G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, buildable_iface_init)) - -enum { - PROP_0, - PROP_ALWAYS_SHOW_TABS, - PROP_CAN_REPLAY, - PROP_CAN_SAVE, - PROP_CURRENT, - N_PROPS -}; - -static GParamSpec *properties [N_PROPS]; - -/** - * sysprof_notebook_new: - * - * Create a new #SysprofNotebook. - * - * Returns: (transfer full): a newly created #SysprofNotebook - * - * Since: 3.34 - */ -GtkWidget * -sysprof_notebook_new (void) -{ - return g_object_new (SYSPROF_TYPE_NOTEBOOK, NULL); -} - -static void -sysprof_notebook_notify_can_save_cb (SysprofNotebook *self, - GParamSpec *pspec, - SysprofDisplay *display) -{ - g_assert (SYSPROF_IS_NOTEBOOK (self)); - g_assert (SYSPROF_IS_DISPLAY (display)); - - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CAN_SAVE]); -} - -static void -sysprof_notebook_notify_can_replay_cb (SysprofNotebook *self, - GParamSpec *pspec, - SysprofDisplay *display) -{ - g_assert (SYSPROF_IS_NOTEBOOK (self)); - g_assert (SYSPROF_IS_DISPLAY (display)); - - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CAN_REPLAY]); -} - -static void -sysprof_notebook_page_added (SysprofNotebook *self, - GtkWidget *child, - guint page_num, - GtkNotebook *notebook) -{ - SysprofNotebookPrivate *priv = sysprof_notebook_get_instance_private (self); - - g_assert (SYSPROF_IS_NOTEBOOK (self)); - g_assert (GTK_IS_WIDGET (child)); - g_assert (GTK_IS_NOTEBOOK (notebook)); - - gtk_notebook_set_show_tabs (notebook, - (priv->always_show_tabs || - gtk_notebook_get_n_pages (notebook) > 1)); - - if (SYSPROF_IS_DISPLAY (child)) - { - GtkWidget *tab = sysprof_tab_new (SYSPROF_DISPLAY (child)); - - gtk_notebook_set_tab_label (notebook, child, tab); - gtk_notebook_set_tab_reorderable (notebook, child, TRUE); - - g_signal_connect_object (child, - "notify::can-replay", - G_CALLBACK (sysprof_notebook_notify_can_replay_cb), - self, - G_CONNECT_SWAPPED); - - g_signal_connect_object (child, - "notify::can-save", - G_CALLBACK (sysprof_notebook_notify_can_save_cb), - self, - G_CONNECT_SWAPPED); - - g_object_notify_by_pspec (G_OBJECT (notebook), properties [PROP_CAN_REPLAY]); - g_object_notify_by_pspec (G_OBJECT (notebook), properties [PROP_CAN_SAVE]); - g_object_notify_by_pspec (G_OBJECT (notebook), properties [PROP_CURRENT]); - - _sysprof_display_focus_record (SYSPROF_DISPLAY (child)); - } -} - -static void -sysprof_notebook_page_removed (SysprofNotebook *self, - GtkWidget *child, - guint page_num, - GtkNotebook *notebook) -{ - SysprofNotebookPrivate *priv = sysprof_notebook_get_instance_private (self); - - g_assert (SYSPROF_IS_NOTEBOOK (self)); - g_assert (GTK_IS_WIDGET (child)); - g_assert (GTK_IS_NOTEBOOK (notebook)); - - if (gtk_widget_in_destruction (GTK_WIDGET (notebook))) - return; - - if (gtk_notebook_get_n_pages (notebook) == 0) - { - child = sysprof_display_new (); - gtk_notebook_append_page (notebook, child, NULL); - gtk_widget_show (child); - - g_signal_handlers_disconnect_by_func (child, - G_CALLBACK (sysprof_notebook_notify_can_save_cb), - notebook); - - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CAN_REPLAY]); - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CAN_SAVE]); - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CURRENT]); - } - - gtk_notebook_set_show_tabs (notebook, - (priv->always_show_tabs || - gtk_notebook_get_n_pages (notebook) > 1)); -} - -static void -sysprof_notebook_switch_page (SysprofNotebook *self, - GtkWidget *widget, - guint page, - GtkNotebook *notebook) -{ - g_assert (SYSPROF_IS_NOTEBOOK (self)); - g_assert (GTK_IS_NOTEBOOK (notebook)); - g_assert (GTK_IS_WIDGET (widget)); - - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CAN_REPLAY]); - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CAN_SAVE]); - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CURRENT]); -} - -static void -sysprof_notebook_dispose (GObject *object) -{ - SysprofNotebook *self = (SysprofNotebook *)object; - SysprofNotebookPrivate *priv = sysprof_notebook_get_instance_private (self); - - if (priv->notebook) - { - gtk_widget_unparent (GTK_WIDGET (priv->notebook)); - priv->notebook = NULL; - } - - G_OBJECT_CLASS (sysprof_notebook_parent_class)->dispose (object); -} - -static void -sysprof_notebook_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - SysprofNotebook *self = (SysprofNotebook *)object; - - switch (prop_id) - { - case PROP_ALWAYS_SHOW_TABS: - g_value_set_boolean (value, sysprof_notebook_get_always_show_tabs (self)); - break; - - case PROP_CAN_REPLAY: - g_value_set_boolean (value, sysprof_notebook_get_can_replay (self)); - break; - - case PROP_CAN_SAVE: - g_value_set_boolean (value, sysprof_notebook_get_can_save (self)); - break; - - case PROP_CURRENT: - g_value_set_object (value, sysprof_notebook_get_current (self)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_notebook_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - SysprofNotebook *self = (SysprofNotebook *)object; - - switch (prop_id) - { - case PROP_ALWAYS_SHOW_TABS: - sysprof_notebook_set_always_show_tabs (self, g_value_get_boolean (value)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_notebook_class_init (SysprofNotebookClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); - - object_class->dispose = sysprof_notebook_dispose; - object_class->get_property = sysprof_notebook_get_property; - object_class->set_property = sysprof_notebook_set_property; - - properties [PROP_ALWAYS_SHOW_TABS] = - g_param_spec_boolean ("always-show-tabs", - "Always Show Tabs", - "Always Show Tabs", - FALSE, - (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); - - properties [PROP_CAN_REPLAY] = - g_param_spec_boolean ("can-replay", - "Can Replay", - "If the current display can replay a recording", - FALSE, - (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - - properties [PROP_CAN_SAVE] = - g_param_spec_boolean ("can-save", - "Can Save", - "If the current display can save a recording", - FALSE, - (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - - properties [PROP_CURRENT] = - g_param_spec_object ("current", - "Current", - "The current display", - SYSPROF_TYPE_DISPLAY, - (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_properties (object_class, N_PROPS, properties); - - gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT); -} - -static void -sysprof_notebook_init (SysprofNotebook *self) -{ - SysprofNotebookPrivate *priv = sysprof_notebook_get_instance_private (self); - - priv->notebook = GTK_NOTEBOOK (gtk_notebook_new ()); - gtk_widget_set_parent (GTK_WIDGET (priv->notebook), GTK_WIDGET (self)); - - gtk_notebook_set_show_border (priv->notebook, FALSE); - gtk_notebook_set_scrollable (priv->notebook, TRUE); - gtk_notebook_popup_enable (priv->notebook); - - g_signal_connect_object (priv->notebook, - "page-added", - G_CALLBACK (sysprof_notebook_page_added), - self, - G_CONNECT_SWAPPED); - - g_signal_connect_object (priv->notebook, - "page-removed", - G_CALLBACK (sysprof_notebook_page_removed), - self, - G_CONNECT_SWAPPED); - - g_signal_connect_object (priv->notebook, - "switch-page", - G_CALLBACK (sysprof_notebook_switch_page), - self, - G_CONNECT_SWAPPED | G_CONNECT_AFTER); -} - -void -sysprof_notebook_close_current (SysprofNotebook *self) -{ - SysprofNotebookPrivate *priv = sysprof_notebook_get_instance_private (self); - gint page; - - g_return_if_fail (SYSPROF_IS_NOTEBOOK (self)); - - if ((page = gtk_notebook_get_current_page (priv->notebook)) >= 0) - gtk_notebook_remove_page (priv->notebook, page); -} - -void -sysprof_notebook_open (SysprofNotebook *self, - GFile *file) -{ - SysprofNotebookPrivate *priv = sysprof_notebook_get_instance_private (self); - SysprofDisplay *display = NULL; - int page; - - g_return_if_fail (SYSPROF_IS_NOTEBOOK (self)); - g_return_if_fail (g_file_is_native (file)); - - for (page = 0; page < sysprof_notebook_get_n_pages (self); page++) - { - SysprofDisplay *child = sysprof_notebook_get_nth_page (self, page); - - if (sysprof_display_is_empty (child)) - { - display = child; - break; - } - } - - if (display == NULL) - { - display = SYSPROF_DISPLAY (sysprof_display_new ()); - page = sysprof_notebook_append (self, display); - } - else - { - page = gtk_notebook_page_num (priv->notebook, GTK_WIDGET (display)); - } - - sysprof_notebook_set_current_page (self, page); - - sysprof_display_open (SYSPROF_DISPLAY (display), file); -} - -SysprofDisplay * -sysprof_notebook_get_current (SysprofNotebook *self) -{ - SysprofNotebookPrivate *priv = sysprof_notebook_get_instance_private (self); - gint page; - - g_assert (SYSPROF_IS_NOTEBOOK (self)); - - if ((page = gtk_notebook_get_current_page (priv->notebook)) >= 0) - return SYSPROF_DISPLAY (gtk_notebook_get_nth_page (priv->notebook, page)); - - return NULL; -} - -void -sysprof_notebook_save (SysprofNotebook *self) -{ - SysprofDisplay *display; - - g_return_if_fail (SYSPROF_IS_NOTEBOOK (self)); - - if ((display = sysprof_notebook_get_current (self))) - sysprof_display_save (display); -} - -gboolean -sysprof_notebook_get_can_save (SysprofNotebook *self) -{ - SysprofDisplay *display; - - g_return_val_if_fail (SYSPROF_IS_NOTEBOOK (self), FALSE); - - if ((display = sysprof_notebook_get_current (self))) - return sysprof_display_get_can_save (display); - - return FALSE; -} - -gboolean -sysprof_notebook_get_can_replay (SysprofNotebook *self) -{ - SysprofDisplay *display; - - g_return_val_if_fail (SYSPROF_IS_NOTEBOOK (self), FALSE); - - if ((display = sysprof_notebook_get_current (self))) - return sysprof_display_get_can_replay (display); - - return FALSE; -} - -void -sysprof_notebook_replay (SysprofNotebook *self) -{ - SysprofNotebookPrivate *priv = sysprof_notebook_get_instance_private (self); - SysprofDisplay *display; - SysprofDisplay *replay; - gint page; - - g_return_if_fail (SYSPROF_IS_NOTEBOOK (self)); - - if (!(display = sysprof_notebook_get_current (self)) || - !sysprof_display_get_can_replay (display) || - !(replay = sysprof_display_replay (display))) - return; - - g_return_if_fail (SYSPROF_IS_DISPLAY (replay)); - - gtk_widget_show (GTK_WIDGET (replay)); - gtk_notebook_append_page (priv->notebook, GTK_WIDGET (replay), NULL); - page = gtk_notebook_page_num (priv->notebook, GTK_WIDGET (replay)); - gtk_notebook_set_current_page (priv->notebook, page); -} - -void -sysprof_notebook_add_profiler (SysprofNotebook *self, - SysprofProfiler *profiler) -{ - SysprofNotebookPrivate *priv = sysprof_notebook_get_instance_private (self); - GtkWidget *display; - gint page; - - g_return_if_fail (SYSPROF_IS_NOTEBOOK (self)); - g_return_if_fail (SYSPROF_IS_PROFILER (profiler)); - - display = sysprof_display_new_for_profiler (profiler); - - gtk_widget_show (display); - gtk_notebook_append_page (priv->notebook, GTK_WIDGET (display), NULL); - page = gtk_notebook_page_num (priv->notebook, display); - gtk_notebook_set_current_page (priv->notebook, page); -} - -gboolean -sysprof_notebook_get_always_show_tabs (SysprofNotebook *self) -{ - SysprofNotebookPrivate *priv = sysprof_notebook_get_instance_private (self); - - g_return_val_if_fail (SYSPROF_IS_NOTEBOOK (self), FALSE); - - return priv->always_show_tabs; -} - -void -sysprof_notebook_set_always_show_tabs (SysprofNotebook *self, - gboolean always_show_tabs) -{ - SysprofNotebookPrivate *priv = sysprof_notebook_get_instance_private (self); - - g_return_if_fail (SYSPROF_IS_NOTEBOOK (self)); - - always_show_tabs = !!always_show_tabs; - - if (always_show_tabs != priv->always_show_tabs) - { - priv->always_show_tabs = always_show_tabs; - gtk_notebook_set_show_tabs (priv->notebook, - (priv->always_show_tabs || - gtk_notebook_get_n_pages (priv->notebook) > 1)); - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ALWAYS_SHOW_TABS]); - } -} - -static void -sysprof_notebook_add_child (GtkBuildable *buildable, - GtkBuilder *builder, - GObject *child, - const char *type) -{ - SysprofNotebook *self = (SysprofNotebook *)buildable; - SysprofNotebookPrivate *priv = sysprof_notebook_get_instance_private (self); - - g_assert (SYSPROF_IS_NOTEBOOK (self)); - - if (SYSPROF_IS_DISPLAY (child)) - gtk_notebook_append_page (priv->notebook, GTK_WIDGET (child), NULL); - else - g_warning ("Cannot add child of type %s to %s", - G_OBJECT_TYPE_NAME (child), - G_OBJECT_TYPE_NAME (self)); -} - -static void -buildable_iface_init (GtkBuildableIface *iface) -{ - iface->add_child = sysprof_notebook_add_child; -} - -guint -sysprof_notebook_get_n_pages (SysprofNotebook *self) -{ - SysprofNotebookPrivate *priv = sysprof_notebook_get_instance_private (self); - - g_return_val_if_fail (SYSPROF_IS_NOTEBOOK (self), 0); - - return gtk_notebook_get_n_pages (priv->notebook); -} - -SysprofDisplay * -sysprof_notebook_get_nth_page (SysprofNotebook *self, - guint nth) -{ - SysprofNotebookPrivate *priv = sysprof_notebook_get_instance_private (self); - - g_return_val_if_fail (SYSPROF_IS_NOTEBOOK (self), NULL); - - return SYSPROF_DISPLAY (gtk_notebook_get_nth_page (priv->notebook, nth)); -} - -void -sysprof_notebook_set_current_page (SysprofNotebook *self, - int nth) -{ - SysprofNotebookPrivate *priv = sysprof_notebook_get_instance_private (self); - - g_return_if_fail (SYSPROF_IS_NOTEBOOK (self)); - - gtk_notebook_set_current_page (priv->notebook, nth); -} - -int -sysprof_notebook_append (SysprofNotebook *self, - SysprofDisplay *display) -{ - SysprofNotebookPrivate *priv = sysprof_notebook_get_instance_private (self); - - g_return_val_if_fail (SYSPROF_IS_NOTEBOOK (self), -1); - g_return_val_if_fail (SYSPROF_IS_DISPLAY (display), -1); - - return gtk_notebook_append_page (priv->notebook, GTK_WIDGET (display), NULL); -} diff --git a/src/libsysprof-ui/sysprof-notebook.h b/src/libsysprof-ui/sysprof-notebook.h deleted file mode 100644 index 943dc1f1..00000000 --- a/src/libsysprof-ui/sysprof-notebook.h +++ /dev/null @@ -1,85 +0,0 @@ -/* sysprof-notebook.h - * - * Copyright 2019 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 - -#if !defined (SYSPROF_UI_INSIDE) && !defined (SYSPROF_UI_COMPILATION) -# error "Only can be included directly." -#endif - -#include -#include - -#include "sysprof-display.h" -#include "sysprof-version-macros.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_NOTEBOOK (sysprof_notebook_get_type()) - -SYSPROF_AVAILABLE_IN_ALL -G_DECLARE_DERIVABLE_TYPE (SysprofNotebook, sysprof_notebook, SYSPROF, NOTEBOOK, GtkWidget) - -struct _SysprofNotebookClass -{ - GtkWidgetClass parent_class; - - /*< private >*/ - gpointer _reserved[16]; -}; - -SYSPROF_AVAILABLE_IN_ALL -GtkWidget *sysprof_notebook_new (void); -SYSPROF_AVAILABLE_IN_ALL -SysprofDisplay *sysprof_notebook_get_current (SysprofNotebook *self); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_notebook_close_current (SysprofNotebook *self); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_notebook_open (SysprofNotebook *self, - GFile *file); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_notebook_save (SysprofNotebook *self); -SYSPROF_AVAILABLE_IN_ALL -gboolean sysprof_notebook_get_can_save (SysprofNotebook *self); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_notebook_replay (SysprofNotebook *self); -SYSPROF_AVAILABLE_IN_ALL -gboolean sysprof_notebook_get_can_replay (SysprofNotebook *self); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_notebook_add_profiler (SysprofNotebook *self, - SysprofProfiler *profiler); -SYSPROF_AVAILABLE_IN_ALL -gboolean sysprof_notebook_get_always_show_tabs (SysprofNotebook *self); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_notebook_set_always_show_tabs (SysprofNotebook *self, - gboolean always_show_tabs); -SYSPROF_AVAILABLE_IN_ALL -guint sysprof_notebook_get_n_pages (SysprofNotebook *self); -SYSPROF_AVAILABLE_IN_ALL -SysprofDisplay *sysprof_notebook_get_nth_page (SysprofNotebook *self, - guint nth); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_notebook_set_current_page (SysprofNotebook *self, - int page); -SYSPROF_AVAILABLE_IN_ALL -int sysprof_notebook_append (SysprofNotebook *self, - SysprofDisplay *display); - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-page.c b/src/libsysprof-ui/sysprof-page.c deleted file mode 100644 index 45f61152..00000000 --- a/src/libsysprof-ui/sysprof-page.c +++ /dev/null @@ -1,256 +0,0 @@ -/* sysprof-page.c - * - * Copyright 2019 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 - */ - -#define G_LOG_DOMAIN "sysprof-page" - -#include "config.h" - -#include "sysprof-display.h" -#include "sysprof-page.h" -#include "sysprof-ui-private.h" - -typedef struct -{ - gchar *title; -} SysprofPagePrivate; - -G_DEFINE_TYPE_WITH_PRIVATE (SysprofPage, sysprof_page, GTK_TYPE_WIDGET) - -enum { - PROP_0, - PROP_TITLE, - N_PROPS -}; - -static GParamSpec *properties [N_PROPS]; - -/** - * sysprof_page_new: - * - * Create a new #SysprofPage. - * - * Returns: (transfer full) (type SysprofPage): a newly created #SysprofPage - */ -GtkWidget * -sysprof_page_new (void) -{ - return g_object_new (SYSPROF_TYPE_PAGE, NULL); -} - -const gchar * -sysprof_page_get_title (SysprofPage *self) -{ - SysprofPagePrivate *priv = sysprof_page_get_instance_private (self); - - g_return_val_if_fail (SYSPROF_IS_PAGE (self), NULL); - - return priv->title; -} - -void -sysprof_page_set_title (SysprofPage *self, - const gchar *title) -{ - SysprofPagePrivate *priv = sysprof_page_get_instance_private (self); - - g_return_if_fail (SYSPROF_IS_PAGE (self)); - - if (g_strcmp0 (priv->title, title) != 0) - { - g_free (priv->title); - priv->title = g_strdup (title); - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_TITLE]); - } -} - -static void -sysprof_page_real_load_async (SysprofPage *self, - SysprofCaptureReader *reader, - SysprofSelection *selection, - SysprofCaptureCondition *condition, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - g_task_report_new_error (self, callback, user_data, - sysprof_page_load_async, - G_IO_ERROR, - G_IO_ERROR_NOT_SUPPORTED, - "Operation not supported"); -} - -static gboolean -sysprof_page_real_load_finish (SysprofPage *self, - GAsyncResult *result, - GError **error) -{ - return g_task_propagate_boolean (G_TASK (result), error); -} - -static void -sysprof_page_dispose (GObject *object) -{ - SysprofPage *self = (SysprofPage *)object; - SysprofPagePrivate *priv = sysprof_page_get_instance_private (self); - GtkWidget *child; - - g_clear_pointer (&priv->title, g_free); - - while ((child = gtk_widget_get_first_child (GTK_WIDGET (self)))) - gtk_widget_unparent (child); - - G_OBJECT_CLASS (sysprof_page_parent_class)->dispose (object); -} - -static void -sysprof_page_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - SysprofPage *self = SYSPROF_PAGE (object); - - switch (prop_id) - { - case PROP_TITLE: - g_value_set_string (value, sysprof_page_get_title (self)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_page_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - SysprofPage *self = SYSPROF_PAGE (object); - - switch (prop_id) - { - case PROP_TITLE: - sysprof_page_set_title (self, g_value_get_string (value)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_page_class_init (SysprofPageClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); - - object_class->dispose = sysprof_page_dispose; - object_class->get_property = sysprof_page_get_property; - object_class->set_property = sysprof_page_set_property; - - klass->load_async = sysprof_page_real_load_async; - klass->load_finish = sysprof_page_real_load_finish; - - properties [PROP_TITLE] = - g_param_spec_string ("title", - "Title", - "The title for the page", - NULL, - (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_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT); -} - -static void -sysprof_page_init (SysprofPage *self) -{ - gtk_widget_set_vexpand (GTK_WIDGET (self), TRUE); -} - -/** - * sysprof_page_load_async: - * @condition: (nullable): a #sysprofCaptureCondition or %NULL - * @cancellable: (nullable): a #GCancellable or %NULL - * - * Since: 3.34 - */ -void -sysprof_page_load_async (SysprofPage *self, - SysprofCaptureReader *reader, - SysprofSelection *selection, - SysprofCaptureCondition *condition, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - g_return_if_fail (SYSPROF_IS_PAGE (self)); - g_return_if_fail (reader != NULL); - g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); - - SYSPROF_PAGE_GET_CLASS (self)->load_async (self, reader, selection, condition, cancellable, callback, user_data); -} - -gboolean -sysprof_page_load_finish (SysprofPage *self, - GAsyncResult *result, - GError **error) -{ - g_return_val_if_fail (SYSPROF_IS_PAGE (self), FALSE); - g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE); - - return SYSPROF_PAGE_GET_CLASS (self)->load_finish (self, result, error); -} - -void -sysprof_page_set_size_group (SysprofPage *self, - GtkSizeGroup *size_group) -{ - g_return_if_fail (SYSPROF_IS_PAGE (self)); - g_return_if_fail (!size_group || GTK_IS_SIZE_GROUP (size_group)); - - if (SYSPROF_PAGE_GET_CLASS (self)->set_size_group) - SYSPROF_PAGE_GET_CLASS (self)->set_size_group (self, size_group); -} - -void -sysprof_page_set_hadjustment (SysprofPage *self, - GtkAdjustment *hadjustment) -{ - g_return_if_fail (SYSPROF_IS_PAGE (self)); - g_return_if_fail (!hadjustment || GTK_IS_ADJUSTMENT (hadjustment)); - - if (SYSPROF_PAGE_GET_CLASS (self)->set_hadjustment) - SYSPROF_PAGE_GET_CLASS (self)->set_hadjustment (self, hadjustment); -} - -void -sysprof_page_reload (SysprofPage *self) -{ - GtkWidget *display; - - g_return_if_fail (SYSPROF_IS_PAGE (self)); - - if ((display = gtk_widget_get_ancestor (GTK_WIDGET (self), SYSPROF_TYPE_DISPLAY))) - _sysprof_display_reload_page (SYSPROF_DISPLAY (display), self); -} diff --git a/src/libsysprof-ui/sysprof-page.h b/src/libsysprof-ui/sysprof-page.h deleted file mode 100644 index 22d55044..00000000 --- a/src/libsysprof-ui/sysprof-page.h +++ /dev/null @@ -1,88 +0,0 @@ -/* sysprof-page.h - * - * Copyright 2019 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 - -#if !defined (SYSPROF_UI_INSIDE) && !defined (SYSPROF_UI_COMPILATION) -# error "Only can be included directly." -#endif - -#include -#include - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_PAGE (sysprof_page_get_type()) - -SYSPROF_AVAILABLE_IN_ALL -G_DECLARE_DERIVABLE_TYPE (SysprofPage, sysprof_page, SYSPROF, PAGE, GtkWidget) - -struct _SysprofPageClass -{ - GtkWidgetClass parent_class; - - void (*load_async) (SysprofPage *self, - SysprofCaptureReader *reader, - SysprofSelection *selection, - SysprofCaptureCondition *condition, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); - gboolean (*load_finish) (SysprofPage *self, - GAsyncResult *result, - GError **error); - void (*set_hadjustment) (SysprofPage *self, - GtkAdjustment *hadjustment); - void (*set_size_group) (SysprofPage *self, - GtkSizeGroup *size_group); - - /*< private >*/ - gpointer _reserved[16]; -}; - -SYSPROF_AVAILABLE_IN_ALL -GtkWidget *sysprof_page_new (void); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_page_load_async (SysprofPage *self, - SysprofCaptureReader *reader, - SysprofSelection *selection, - SysprofCaptureCondition *condition, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -SYSPROF_AVAILABLE_IN_ALL -gboolean sysprof_page_load_finish (SysprofPage *self, - GAsyncResult *result, - GError **error); -SYSPROF_AVAILABLE_IN_3_36 -void sysprof_page_reload (SysprofPage *self); -SYSPROF_AVAILABLE_IN_ALL -const gchar *sysprof_page_get_title (SysprofPage *self); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_page_set_title (SysprofPage *self, - const gchar *title); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_page_set_hadjustment (SysprofPage *self, - GtkAdjustment *hadjustment); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_page_set_size_group (SysprofPage *self, - GtkSizeGroup *size_group); - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-process-model-row.c b/src/libsysprof-ui/sysprof-process-model-row.c deleted file mode 100644 index aa046f39..00000000 --- a/src/libsysprof-ui/sysprof-process-model-row.c +++ /dev/null @@ -1,258 +0,0 @@ -/* sysprof-process-model-row.c - * - * Copyright 2016-2019 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 - */ - -#define G_LOG_DOMAIN "sysprof-process-model-row" - -#include "config.h" - -#include "sysprof-process-model-row.h" - -typedef struct -{ - SysprofProcessModelItem *item; - - GtkLabel *args_label; - GtkLabel *label; - GtkLabel *pid; - GtkImage *image; - GtkImage *check; -} SysprofProcessModelRowPrivate; - -G_DEFINE_TYPE_WITH_PRIVATE (SysprofProcessModelRow, sysprof_process_model_row, GTK_TYPE_LIST_BOX_ROW) - -enum { - PROP_0, - PROP_ITEM, - PROP_SELECTED, - N_PROPS -}; - -static GParamSpec *properties [N_PROPS]; - -GtkWidget * -sysprof_process_model_row_new (SysprofProcessModelItem *item) -{ - return g_object_new (SYSPROF_TYPE_PROCESS_MODEL_ROW, - "item", item, - NULL); -} - -SysprofProcessModelItem * -sysprof_process_model_row_get_item (SysprofProcessModelRow *self) -{ - SysprofProcessModelRowPrivate *priv = sysprof_process_model_row_get_instance_private (self); - - g_return_val_if_fail (SYSPROF_IS_PROCESS_MODEL_ROW (self), NULL); - - return priv->item; -} - -static void -sysprof_process_model_row_set_item (SysprofProcessModelRow *self, - SysprofProcessModelItem *item) -{ - SysprofProcessModelRowPrivate *priv = sysprof_process_model_row_get_instance_private (self); - - g_assert (SYSPROF_IS_PROCESS_MODEL_ROW (self)); - g_assert (SYSPROF_IS_PROCESS_MODEL_ITEM (item)); - - if (g_set_object (&priv->item, item)) - { - const gchar *command_line; - g_auto(GStrv) parts = NULL; - g_autofree gchar *pidstr = NULL; - const gchar * const *argv; - GPid pid; - - command_line = sysprof_process_model_item_get_command_line (item); - parts = g_strsplit (command_line ?: "", "\n", 0); - gtk_label_set_label (priv->label, parts [0]); - - if ((NULL != (argv = sysprof_process_model_item_get_argv (item))) && (argv[0] != NULL)) - { - g_autofree gchar *argvstr = g_strjoinv (" ", (gchar **)&argv[1]); - g_autofree gchar *escaped = g_markup_escape_text (argvstr, -1); - - gtk_label_set_label (priv->args_label, escaped); - } - - pid = sysprof_process_model_item_get_pid (item); - pidstr = g_strdup_printf ("%u", pid); - gtk_label_set_label (priv->pid, pidstr); - gtk_label_set_use_markup (priv->pid, TRUE); - } -} - -gboolean -sysprof_process_model_row_get_selected (SysprofProcessModelRow *self) -{ - SysprofProcessModelRowPrivate *priv = sysprof_process_model_row_get_instance_private (self); - - g_return_val_if_fail (SYSPROF_IS_PROCESS_MODEL_ROW (self), FALSE); - - return gtk_widget_get_visible (GTK_WIDGET (priv->check)); -} - -void -sysprof_process_model_row_set_selected (SysprofProcessModelRow *self, - gboolean selected) -{ - SysprofProcessModelRowPrivate *priv = sysprof_process_model_row_get_instance_private (self); - - g_return_if_fail (SYSPROF_IS_PROCESS_MODEL_ROW (self)); - - selected = !!selected; - - if (selected != sysprof_process_model_row_get_selected (self)) - { - gtk_widget_set_visible (GTK_WIDGET (priv->check), selected); - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_SELECTED]); - } -} - -static gboolean -sysprof_process_model_row_query_tooltip (GtkWidget *widget, - gint x, - gint y, - gboolean keyboard_mode, - GtkTooltip *tooltip) -{ - SysprofProcessModelRow *self = (SysprofProcessModelRow *)widget; - SysprofProcessModelRowPrivate *priv = sysprof_process_model_row_get_instance_private (self); - - g_assert (SYSPROF_IS_PROCESS_MODEL_ROW (self)); - g_assert (GTK_IS_TOOLTIP (tooltip)); - - if (priv->item != NULL) - { - const gchar * const *argv = sysprof_process_model_item_get_argv (priv->item); - - if (argv != NULL) - { - g_autofree gchar *str = g_strjoinv (" ", (gchar **)argv); - gtk_tooltip_set_text (tooltip, str); - return TRUE; - } - } - - return FALSE; -} - -static void -sysprof_process_model_row_finalize (GObject *object) -{ - SysprofProcessModelRow *self = (SysprofProcessModelRow *)object; - SysprofProcessModelRowPrivate *priv = sysprof_process_model_row_get_instance_private (self); - - g_clear_object (&priv->item); - - G_OBJECT_CLASS (sysprof_process_model_row_parent_class)->finalize (object); -} - -static void -sysprof_process_model_row_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - SysprofProcessModelRow *self = SYSPROF_PROCESS_MODEL_ROW (object); - - switch (prop_id) - { - case PROP_ITEM: - g_value_set_object (value, sysprof_process_model_row_get_item (self)); - break; - - case PROP_SELECTED: - g_value_set_boolean (value, sysprof_process_model_row_get_selected (self)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_process_model_row_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - SysprofProcessModelRow *self = SYSPROF_PROCESS_MODEL_ROW (object); - - switch (prop_id) - { - case PROP_ITEM: - sysprof_process_model_row_set_item (self, g_value_get_object (value)); - break; - - case PROP_SELECTED: - sysprof_process_model_row_set_selected (self, g_value_get_boolean (value)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_process_model_row_class_init (SysprofProcessModelRowClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); - - object_class->finalize = sysprof_process_model_row_finalize; - object_class->get_property = sysprof_process_model_row_get_property; - object_class->set_property = sysprof_process_model_row_set_property; - - widget_class->query_tooltip = sysprof_process_model_row_query_tooltip; - - properties [PROP_ITEM] = - g_param_spec_object ("item", - "Item", - "Item", - SYSPROF_TYPE_PROCESS_MODEL_ITEM, - (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - properties [PROP_SELECTED] = - g_param_spec_boolean ("selected", - "Selected", - "Selected", - FALSE, - (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/ui/sysprof-process-model-row.ui"); - gtk_widget_class_bind_template_child_private (widget_class, SysprofProcessModelRow, args_label); - gtk_widget_class_bind_template_child_private (widget_class, SysprofProcessModelRow, image); - gtk_widget_class_bind_template_child_private (widget_class, SysprofProcessModelRow, label); - gtk_widget_class_bind_template_child_private (widget_class, SysprofProcessModelRow, pid); - gtk_widget_class_bind_template_child_private (widget_class, SysprofProcessModelRow, check); -} - -static void -sysprof_process_model_row_init (SysprofProcessModelRow *self) -{ - gtk_widget_init_template (GTK_WIDGET (self)); - - gtk_widget_set_has_tooltip (GTK_WIDGET (self), TRUE); -} diff --git a/src/libsysprof-ui/sysprof-process-model-row.h b/src/libsysprof-ui/sysprof-process-model-row.h deleted file mode 100644 index 63100357..00000000 --- a/src/libsysprof-ui/sysprof-process-model-row.h +++ /dev/null @@ -1,54 +0,0 @@ -/* sysprof-process-model-row.h - * - * Copyright 2016-2019 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 - -#if !defined (SYSPROF_UI_INSIDE) && !defined (SYSPROF_UI_COMPILATION) -# error "Only can be included directly." -#endif - -#include -#include - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_PROCESS_MODEL_ROW (sysprof_process_model_row_get_type()) - -SYSPROF_AVAILABLE_IN_ALL -G_DECLARE_DERIVABLE_TYPE (SysprofProcessModelRow, sysprof_process_model_row, SYSPROF, PROCESS_MODEL_ROW, GtkListBoxRow) - -struct _SysprofProcessModelRowClass -{ - GtkListBoxRowClass parent; - - gpointer padding[4]; -}; - -SYSPROF_AVAILABLE_IN_ALL -GtkWidget *sysprof_process_model_row_new (SysprofProcessModelItem *item); -SYSPROF_AVAILABLE_IN_ALL -SysprofProcessModelItem *sysprof_process_model_row_get_item (SysprofProcessModelRow *self); -SYSPROF_AVAILABLE_IN_ALL -gboolean sysprof_process_model_row_get_selected (SysprofProcessModelRow *self); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_process_model_row_set_selected (SysprofProcessModelRow *self, - gboolean selected); - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-process-model-row.ui b/src/libsysprof-ui/sysprof-process-model-row.ui deleted file mode 100644 index ee23e296..00000000 --- a/src/libsysprof-ui/sysprof-process-model-row.ui +++ /dev/null @@ -1,52 +0,0 @@ - - - - diff --git a/src/libsysprof-ui/sysprof-procs-visualizer.c b/src/libsysprof-ui/sysprof-procs-visualizer.c deleted file mode 100644 index 4c09cb33..00000000 --- a/src/libsysprof-ui/sysprof-procs-visualizer.c +++ /dev/null @@ -1,296 +0,0 @@ -/* sysprof-procs-visualizer.c - * - * Copyright 2019 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 - */ - -#define G_LOG_DOMAIN "sysprof-procs-visualizer" - -#include "config.h" - -#include - -#include "pointcache.h" -#include "sysprof-procs-visualizer.h" - -typedef struct -{ - volatile gint ref_count; - guint n_procs; - guint max_n_procs; - gint64 begin_time; - gint64 end_time; - gint64 duration; - PointCache *cache; - SysprofCaptureCursor *cursor; -} Discovery; - -struct _SysprofProcsVisualizer -{ - SysprofVisualizer parent_instance; - Discovery *discovery; -}; - -G_DEFINE_TYPE (SysprofProcsVisualizer, sysprof_procs_visualizer, SYSPROF_TYPE_VISUALIZER) - -static void -discovery_unref (Discovery *d) -{ - if (g_atomic_int_dec_and_test (&d->ref_count)) - { - g_clear_pointer (&d->cache, point_cache_unref); - g_clear_pointer (&d->cursor, sysprof_capture_cursor_unref); - g_slice_free (Discovery, d); - } -} - -static Discovery * -discovery_ref (Discovery *d) -{ - g_atomic_int_inc (&d->ref_count); - return d; -} - -static bool -discover_max_cb (const SysprofCaptureFrame *frame, - gpointer user_data) -{ - Discovery *d = user_data; - - g_assert (frame != NULL); - g_assert (d != NULL); - - if (frame->type == SYSPROF_CAPTURE_FRAME_PROCESS) - d->n_procs++; - else if (frame->type == SYSPROF_CAPTURE_FRAME_EXIT) - d->n_procs--; - - if (d->n_procs > d->max_n_procs) - d->max_n_procs = d->n_procs; - - return TRUE; -} - -static bool -calc_points_cb (const SysprofCaptureFrame *frame, - gpointer user_data) -{ - Discovery *d = user_data; - gdouble x, y; - - g_assert (frame != NULL); - g_assert (d != NULL); - - if (frame->type == SYSPROF_CAPTURE_FRAME_PROCESS) - d->n_procs++; - else if (frame->type == SYSPROF_CAPTURE_FRAME_EXIT) - d->n_procs--; - - x = (frame->time - d->begin_time) / (gdouble)d->duration; - y = (d->n_procs / (gdouble)d->max_n_procs) * .85; - - point_cache_add_point_to_set (d->cache, 1, x, y); - - return TRUE; -} - -static void -discovery_worker (GTask *task, - gpointer source_object, - gpointer task_data, - GCancellable *cancellable) -{ - Discovery *d = task_data; - - g_assert (G_IS_TASK (task)); - g_assert (SYSPROF_IS_PROCS_VISUALIZER (source_object)); - - sysprof_capture_cursor_foreach (d->cursor, discover_max_cb, d); - - d->n_procs = 0; - sysprof_capture_cursor_reset (d->cursor); - - sysprof_capture_cursor_foreach (d->cursor, calc_points_cb, d); - - g_task_return_pointer (task, - discovery_ref (d), - (GDestroyNotify) discovery_unref); -} - -static void -handle_data_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - SysprofProcsVisualizer *self = (SysprofProcsVisualizer *)object; - Discovery *d; - - g_assert (SYSPROF_IS_PROCS_VISUALIZER (self)); - g_assert (G_IS_TASK (result)); - - if ((d = g_task_propagate_pointer (G_TASK (result), NULL))) - { - g_clear_pointer (&self->discovery, discovery_unref); - self->discovery = g_steal_pointer (&d); - gtk_widget_queue_allocate (GTK_WIDGET (self)); - } -} - -static void -sysprof_procs_visualizer_set_reader (SysprofVisualizer *visualizer, - SysprofCaptureReader *reader) -{ - static const SysprofCaptureFrameType types[] = { - SYSPROF_CAPTURE_FRAME_PROCESS, - SYSPROF_CAPTURE_FRAME_EXIT, - }; - SysprofProcsVisualizer *self = (SysprofProcsVisualizer *)visualizer; - g_autoptr(GTask) task = NULL; - Discovery *d; - - g_assert (SYSPROF_IS_PROCS_VISUALIZER (self)); - g_assert (reader != NULL); - - d = g_slice_new0 (Discovery); - d->ref_count = 1; - d->cache = point_cache_new (); - d->begin_time = sysprof_capture_reader_get_start_time (reader); - d->end_time = sysprof_capture_reader_get_end_time (reader); - d->cursor = sysprof_capture_cursor_new (reader); - d->duration = d->end_time - d->begin_time; - - point_cache_add_set (d->cache, 1); - sysprof_capture_cursor_add_condition (d->cursor, - sysprof_capture_condition_new_where_type_in (G_N_ELEMENTS (types), types)); - - task = g_task_new (self, NULL, handle_data_cb, NULL); - g_task_set_source_tag (task, sysprof_procs_visualizer_set_reader); - g_task_set_task_data (task, d, (GDestroyNotify) discovery_unref); - g_task_run_in_thread (task, discovery_worker); -} - -static void -sysprof_procs_visualizer_snapshot (GtkWidget *widget, - GtkSnapshot *snapshot) -{ - SysprofProcsVisualizer *self = (SysprofProcsVisualizer *)widget; - g_autofree SysprofVisualizerAbsolutePoint *points = NULL; - GtkAllocation alloc; - GdkRGBA background; - GdkRGBA foreground; - const Point *fpoints; - guint n_fpoints = 0; - Discovery *d; - cairo_t *cr; - gdouble last_x = 0; - gdouble last_y = 0; - - g_assert (SYSPROF_IS_PROCS_VISUALIZER (self)); - g_assert (snapshot != NULL); - - gtk_widget_get_allocation (widget, &alloc); - - gdk_rgba_parse (&foreground, "#813d9c"); - background = foreground; - background.alpha *= .5; - - GTK_WIDGET_CLASS (sysprof_procs_visualizer_parent_class)->snapshot (widget, snapshot); - - if (!(d = self->discovery) || d->cache == NULL) - return; - - if (!(fpoints = point_cache_get_points (d->cache, 1, &n_fpoints))) - return; - - /* This is all going to need offscreen drawing instead of new cairo every frame */ - - cr = gtk_snapshot_append_cairo (snapshot, &GRAPHENE_RECT_INIT (0, 0, alloc.width, alloc.height)); - points = g_new0 (SysprofVisualizerAbsolutePoint, n_fpoints); - - sysprof_visualizer_translate_points (SYSPROF_VISUALIZER (self), - (const SysprofVisualizerRelativePoint *)fpoints, - n_fpoints, - points, - n_fpoints); - - last_x = points[0].x; - last_y = points[0].y; - - cairo_move_to (cr, last_x, alloc.height); - cairo_line_to (cr, last_x, last_y); - - for (guint i = 1; i < n_fpoints; i++) - { - cairo_curve_to (cr, - last_x + ((points[i].x - last_x) / 2), - last_y, - last_x + ((points[i].x - last_x) / 2), - points[i].y, - points[i].x, - points[i].y); - - last_x = points[i].x; - last_y = points[i].y; - } - - cairo_line_to (cr, last_x, alloc.height); - cairo_close_path (cr); - - cairo_set_line_width (cr, 1.0); - - gdk_cairo_set_source_rgba (cr, &background); - cairo_fill_preserve (cr); - gdk_cairo_set_source_rgba (cr, &foreground); - cairo_stroke (cr); - - cairo_destroy (cr); -} - -static void -sysprof_procs_visualizer_finalize (GObject *object) -{ - SysprofProcsVisualizer *self = (SysprofProcsVisualizer *)object; - - g_clear_pointer (&self->discovery, discovery_unref); - - G_OBJECT_CLASS (sysprof_procs_visualizer_parent_class)->finalize (object); -} - -static void -sysprof_procs_visualizer_class_init (SysprofProcsVisualizerClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); - SysprofVisualizerClass *visualizer_class = SYSPROF_VISUALIZER_CLASS (klass); - - object_class->finalize = sysprof_procs_visualizer_finalize; - - widget_class->snapshot = sysprof_procs_visualizer_snapshot; - - visualizer_class->set_reader = sysprof_procs_visualizer_set_reader; -} - -static void -sysprof_procs_visualizer_init (SysprofProcsVisualizer *self) -{ -} - -SysprofVisualizer * -sysprof_procs_visualizer_new (void) -{ - return g_object_new (SYSPROF_TYPE_PROCS_VISUALIZER, NULL); -} diff --git a/src/libsysprof-ui/sysprof-procs-visualizer.h b/src/libsysprof-ui/sysprof-procs-visualizer.h deleted file mode 100644 index c7679754..00000000 --- a/src/libsysprof-ui/sysprof-procs-visualizer.h +++ /dev/null @@ -1,33 +0,0 @@ -/* sysprof-procs-visualizer.h - * - * Copyright 2019 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-visualizer.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_PROCS_VISUALIZER (sysprof_procs_visualizer_get_type()) - -G_DECLARE_FINAL_TYPE (SysprofProcsVisualizer, sysprof_procs_visualizer, SYSPROF, PROCS_VISUALIZER, SysprofVisualizer) - -SysprofVisualizer *sysprof_procs_visualizer_new (void); - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-profiler-assistant.c b/src/libsysprof-ui/sysprof-profiler-assistant.c deleted file mode 100644 index e6721e3b..00000000 --- a/src/libsysprof-ui/sysprof-profiler-assistant.c +++ /dev/null @@ -1,493 +0,0 @@ -/* sysprof-profiler-assistant.c - * - * Copyright 2019 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 - */ - -#define G_LOG_DOMAIN "sysprof-profiler-assistant" - -#include "config.h" - -#include -#include - -#include "sysprof-platform.h" - -#include "sysprof-aid-icon.h" -#include "sysprof-control-source.h" -#include "sysprof-environ-editor.h" -#include "sysprof-model-filter.h" -#include "sysprof-profiler-assistant.h" -#include "sysprof-process-model-row.h" -#include "sysprof-theme-manager.h" -#include "sysprof-ui-private.h" - -#include "sysprof-battery-aid.h" -#include "sysprof-callgraph-aid.h" -#include "sysprof-cpu-aid.h" -#include "sysprof-memory-aid.h" -#include "sysprof-memprof-aid.h" -#include "sysprof-netdev-aid.h" -#include "sysprof-proxy-aid.h" -#include "sysprof-rapl-aid.h" - -struct _SysprofProfilerAssistant -{ - GtkWidget parent_instance; - - SysprofProcessModel *process_model; - - /* Template Objects */ - GtkSwitch *allow_throttling; - GtkButton *record_button; - AdwEntryRow *command_line; - GtkSearchEntry *search_entry; - GtkListBox *process_list_box; - SysprofEnvironEditor *environ_editor; - GtkFlowBox *aid_flow_box; - GtkSwitch *whole_system_switch; - GtkSwitch *launch_switch; - GtkSwitch *inherit_switch; -}; - -enum { - START_RECORDING, - N_SIGNALS -}; - -static guint signals [N_SIGNALS]; - -G_DEFINE_TYPE (SysprofProfilerAssistant, sysprof_profiler_assistant, GTK_TYPE_WIDGET) - -/** - * sysprof_profiler_assistant_new: - * - * Create a new #SysprofProfilerAssistant. - * - * Returns: (transfer full): a newly created #SysprofProfilerAssistant - * - * Since: 3.34 - */ -GtkWidget * -sysprof_profiler_assistant_new (void) -{ - return g_object_new (SYSPROF_TYPE_PROFILER_ASSISTANT, NULL); -} - -static void -sysprof_profiler_assistant_aid_activated_cb (SysprofProfilerAssistant *self, - SysprofAidIcon *icon, - GtkFlowBox *flow_box) -{ - g_assert (SYSPROF_IS_PROFILER_ASSISTANT (self)); - g_assert (SYSPROF_IS_AID_ICON (icon)); - g_assert (GTK_IS_FLOW_BOX (flow_box)); - - sysprof_aid_icon_toggle (icon); -} - -static GtkWidget * -create_process_row_cb (gpointer item_, - gpointer user_data) -{ - SysprofProcessModelItem *item = item_; - - g_assert (SYSPROF_IS_PROCESS_MODEL_ITEM (item)); - - return sysprof_process_model_row_new (item); -} - -static void -sysprof_profiler_assistant_notify_active_cb (SysprofProfilerAssistant *self, - GParamSpec *pspec, - GtkSwitch *switch_) -{ - g_assert (SYSPROF_IS_PROFILER_ASSISTANT (self)); - g_assert (GTK_IS_SWITCH (switch_)); - - if (gtk_switch_get_active (switch_)) - return; - - if (self->process_model == NULL) - { - self->process_model = sysprof_process_model_new (); - gtk_list_box_bind_model (self->process_list_box, - G_LIST_MODEL (self->process_model), - create_process_row_cb, - NULL, NULL); - sysprof_process_model_reload (self->process_model); - } -} - -static void -sysprof_profiler_assistant_row_activated_cb (SysprofProfilerAssistant *self, - SysprofProcessModelRow *row, - GtkListBox *list_box) -{ - g_assert (SYSPROF_PROFILER_ASSISTANT (self)); - g_assert (SYSPROF_IS_PROCESS_MODEL_ROW (row)); - g_assert (GTK_IS_LIST_BOX (list_box)); - - sysprof_process_model_row_set_selected (row, - !sysprof_process_model_row_get_selected (row)); -} - -static void -sysprof_profiler_assistant_command_line_changed_cb (SysprofProfilerAssistant *self, - GtkEntry *entry) -{ - g_auto(GStrv) argv = NULL; - GtkStyleContext *style_context; - const gchar *text; - gint argc; - - g_assert (SYSPROF_IS_PROFILER_ASSISTANT (self)); - g_assert (ADW_IS_ENTRY_ROW (entry)); - - style_context = gtk_widget_get_style_context (GTK_WIDGET (entry)); - text = gtk_editable_get_text (GTK_EDITABLE (entry)); - - if (text == NULL || text[0] == 0 || g_shell_parse_argv (text, &argc, &argv, NULL)) - gtk_style_context_remove_class (style_context, "error"); - else - gtk_style_context_add_class (style_context, "error"); -} - -static void -sysprof_profiler_assistant_foreach_cb (GtkWidget *widget, - SysprofProfiler *profiler) -{ - g_assert (GTK_IS_WIDGET (widget)); - g_assert (SYSPROF_IS_PROFILER (profiler)); - - if (SYSPROF_IS_PROCESS_MODEL_ROW (widget) && - sysprof_process_model_row_get_selected (SYSPROF_PROCESS_MODEL_ROW (widget))) - { - SysprofProcessModelItem *item; - GPid pid; - - item = sysprof_process_model_row_get_item (SYSPROF_PROCESS_MODEL_ROW (widget)); - pid = sysprof_process_model_item_get_pid (item); - - sysprof_profiler_add_pid (profiler, pid); - } - else if (SYSPROF_IS_AID_ICON (widget)) - { - if (sysprof_aid_icon_is_selected (SYSPROF_AID_ICON (widget))) - { - SysprofAid *aid = sysprof_aid_icon_get_aid (SYSPROF_AID_ICON (widget)); - - sysprof_aid_prepare (aid, profiler); - } - } -} - -static void -sysprof_profiler_assistant_record_clicked_cb (SysprofProfilerAssistant *self, - GtkButton *button) -{ - g_autoptr(SysprofProfiler) profiler = NULL; - g_autoptr(SysprofCaptureWriter) writer = NULL; - g_autoptr(SysprofSource) symbols_source = NULL; -#ifdef __linux__ - g_autoptr(SysprofSource) proc_source = NULL; -#endif - gint fd; - - g_assert (SYSPROF_IS_PROFILER_ASSISTANT (self)); - g_assert (GTK_IS_BUTTON (button)); - - gtk_widget_set_sensitive (GTK_WIDGET (self), FALSE); - - /* Setup a writer immediately */ - if (-1 == (fd = sysprof_memfd_create ("[sysprof-capture]")) || - !(writer = sysprof_capture_writer_new_from_fd (fd, 0))) - { - if (fd != -1) - close (fd); - return; - } - - profiler = sysprof_local_profiler_new (); - sysprof_profiler_set_writer (profiler, writer); - - /* Add pids to profiler */ - for (GtkWidget *child = gtk_widget_get_first_child (GTK_WIDGET (self->process_list_box)); - child; - child = gtk_widget_get_next_sibling (child)) - sysprof_profiler_assistant_foreach_cb (child, profiler); - - /* Setup whole system profiling */ - sysprof_profiler_set_whole_system (profiler, gtk_switch_get_active (self->whole_system_switch)); - - if (gtk_switch_get_active (self->launch_switch)) - { - g_auto(GStrv) argv = NULL; - g_auto(GStrv) env = NULL; - SysprofEnviron *environ_; - const gchar *command; - gint argc; - - command = gtk_editable_get_text (GTK_EDITABLE (self->command_line)); - g_shell_parse_argv (command, &argc, &argv, NULL); - - sysprof_profiler_set_spawn (profiler, TRUE); - sysprof_profiler_set_spawn_argv (profiler, (const gchar * const *)argv); - - environ_ = sysprof_environ_editor_get_environ (self->environ_editor); - env = sysprof_environ_get_environ (environ_); - sysprof_profiler_set_spawn_env (profiler, (const gchar * const *)env); - - sysprof_profiler_set_spawn_inherit_environ (profiler, - gtk_switch_get_active (self->inherit_switch)); - } - -#ifdef __linux__ - /* Always add the proc source */ - proc_source = sysprof_proc_source_new (); - sysprof_profiler_add_source (profiler, proc_source); - - { - g_autoptr(SysprofSource) governor = sysprof_governor_source_new (); - sysprof_governor_source_set_disable_governor (SYSPROF_GOVERNOR_SOURCE (governor), - !gtk_switch_get_active (self->allow_throttling)); - sysprof_profiler_add_source (profiler, governor); - } -#endif - - /* Always add symbol decoder to save to file immediately */ - symbols_source = sysprof_symbols_source_new (); - sysprof_profiler_add_source (profiler, symbols_source); - - /* Now allow the aids to add their sources */ - for (GtkWidget *child = gtk_widget_get_first_child (GTK_WIDGET (self->aid_flow_box)); - child; - child = gtk_widget_get_next_sibling (child)) - sysprof_profiler_assistant_foreach_cb (child, profiler); - - g_signal_emit (self, signals [START_RECORDING], 0, profiler); -} - -static gboolean -filter_by_search_text (GObject *object, - gpointer user_data) -{ - SysprofProcessModelItem *item = SYSPROF_PROCESS_MODEL_ITEM (object); - const gchar *haystack; - const gchar * const *argv; - const gchar *text = user_data; - - haystack = sysprof_process_model_item_get_command_line (item); - - if (haystack) - { - if (strcasestr (haystack, text) != NULL) - return TRUE; - } - - argv = sysprof_process_model_item_get_argv (item); - - if (argv) - { - for (guint i = 0; argv[i]; i++) - { - if (strcasestr (argv[i], text) != NULL) - return TRUE; - } - } - - return FALSE; -} - -static void -sysprof_profiler_assistant_search_changed_cb (SysprofProfilerAssistant *self, - GtkEditable *search_entry) -{ - g_autoptr(SysprofModelFilter) filter = NULL; - const char *text; - - g_assert (SYSPROF_IS_PROFILER_ASSISTANT (self)); - g_assert (GTK_IS_EDITABLE (search_entry)); - - if (self->process_model == NULL) - return; - - sysprof_process_model_queue_reload (self->process_model); - - text = gtk_editable_get_text (GTK_EDITABLE (search_entry)); - - if (text[0] == 0) - { - gtk_list_box_bind_model (self->process_list_box, - G_LIST_MODEL (self->process_model), - create_process_row_cb, - NULL, NULL); - return; - } - - filter = sysprof_model_filter_new (G_LIST_MODEL (self->process_model)); - sysprof_model_filter_set_filter_func (filter, - filter_by_search_text, - g_strdup (text), - g_free); - gtk_list_box_bind_model (self->process_list_box, - G_LIST_MODEL (filter), - create_process_row_cb, - NULL, NULL); -} - -static void -sysprof_profiler_assistant_dispose (GObject *object) -{ - SysprofProfilerAssistant *self = (SysprofProfilerAssistant *)object; - GtkWidget *child; - - g_clear_object (&self->process_model); - - while ((child = gtk_widget_get_first_child (GTK_WIDGET (self)))) - gtk_widget_unparent (child); - - G_OBJECT_CLASS (sysprof_profiler_assistant_parent_class)->dispose (object); -} - -static void -sysprof_profiler_assistant_class_init (SysprofProfilerAssistantClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); - - object_class->dispose = sysprof_profiler_assistant_dispose; - - /** - * SysprofProfilerAssistant::start-recording: - * @self: a #SysprofProfilerAssistant - * @profiler: a #SysprofProfiler - * - * This signal is emitted when a new profiling session should start. - */ - signals [START_RECORDING] = - g_signal_new ("start-recording", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, - 0, NULL, NULL, - g_cclosure_marshal_VOID__OBJECT, - G_TYPE_NONE, 1, SYSPROF_TYPE_PROFILER); - - gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/sysprof/ui/sysprof-profiler-assistant.ui"); - gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT); - gtk_widget_class_bind_template_child (widget_class, SysprofProfilerAssistant, allow_throttling); - gtk_widget_class_bind_template_child (widget_class, SysprofProfilerAssistant, aid_flow_box); - gtk_widget_class_bind_template_child (widget_class, SysprofProfilerAssistant, command_line); - gtk_widget_class_bind_template_child (widget_class, SysprofProfilerAssistant, environ_editor); - gtk_widget_class_bind_template_child (widget_class, SysprofProfilerAssistant, process_list_box); - gtk_widget_class_bind_template_child (widget_class, SysprofProfilerAssistant, record_button); - gtk_widget_class_bind_template_child (widget_class, SysprofProfilerAssistant, whole_system_switch); - gtk_widget_class_bind_template_child (widget_class, SysprofProfilerAssistant, launch_switch); - gtk_widget_class_bind_template_child (widget_class, SysprofProfilerAssistant, inherit_switch); - gtk_widget_class_bind_template_child (widget_class, SysprofProfilerAssistant, search_entry); - - sysprof_theme_manager_register_resource (sysprof_theme_manager_get_default (), - NULL, - NULL, - "/org/gnome/sysprof/css/SysprofProfilerAssistant-shared.css"); - - g_type_ensure (SYSPROF_TYPE_AID_ICON); - g_type_ensure (SYSPROF_TYPE_BATTERY_AID); - g_type_ensure (SYSPROF_TYPE_CALLGRAPH_AID); - g_type_ensure (SYSPROF_TYPE_CONTROL_SOURCE); - g_type_ensure (SYSPROF_TYPE_CPU_AID); - g_type_ensure (SYSPROF_TYPE_DISKSTAT_SOURCE); - g_type_ensure (SYSPROF_TYPE_ENVIRON_EDITOR); - g_type_ensure (SYSPROF_TYPE_MEMORY_AID); - g_type_ensure (SYSPROF_TYPE_MEMPROF_AID); - g_type_ensure (SYSPROF_TYPE_NETDEV_AID); - g_type_ensure (SYSPROF_TYPE_PROXY_AID); - g_type_ensure (SYSPROF_TYPE_RAPL_AID); -} - -static void -sysprof_profiler_assistant_init (SysprofProfilerAssistant *self) -{ - g_autoptr(SysprofEnviron) environ_ = sysprof_environ_new (); - - gtk_widget_init_template (GTK_WIDGET (self)); - - g_signal_connect_object (self->record_button, - "clicked", - G_CALLBACK (sysprof_profiler_assistant_record_clicked_cb), - self, - G_CONNECT_SWAPPED); - - g_signal_connect_object (self->command_line, - "changed", - G_CALLBACK (sysprof_profiler_assistant_command_line_changed_cb), - self, - G_CONNECT_SWAPPED); - - g_signal_connect_object (self->process_list_box, - "row-activated", - G_CALLBACK (sysprof_profiler_assistant_row_activated_cb), - self, - G_CONNECT_SWAPPED); - - g_signal_connect_object (self->whole_system_switch, - "notify::active", - G_CALLBACK (sysprof_profiler_assistant_notify_active_cb), - self, - G_CONNECT_SWAPPED); - - g_signal_connect_object (self->aid_flow_box, - "child-activated", - G_CALLBACK (sysprof_profiler_assistant_aid_activated_cb), - self, - G_CONNECT_SWAPPED); - - g_signal_connect_object (self->search_entry, - "changed", - G_CALLBACK (sysprof_profiler_assistant_search_changed_cb), - self, - G_CONNECT_SWAPPED); - - sysprof_environ_editor_set_environ (self->environ_editor, environ_); -} - -void -_sysprof_profiler_assistant_focus_record (SysprofProfilerAssistant *self) -{ - g_return_if_fail (SYSPROF_IS_PROFILER_ASSISTANT (self)); - - gtk_widget_grab_focus (GTK_WIDGET (self->record_button)); -} - -void -sysprof_profiler_assistant_set_executable (SysprofProfilerAssistant *self, - const gchar *path) -{ - g_return_if_fail (SYSPROF_IS_PROFILER_ASSISTANT (self)); - - if (path == NULL || path[0] == 0) - { - gtk_editable_set_text (GTK_EDITABLE (self->command_line), ""); - gtk_switch_set_active (self->launch_switch, FALSE); - } - else - { - gtk_editable_set_text (GTK_EDITABLE (self->command_line), path); - gtk_switch_set_active (self->launch_switch, TRUE); - gtk_widget_grab_focus (GTK_WIDGET (self->command_line)); - } -} diff --git a/src/libsysprof-ui/sysprof-profiler-assistant.h b/src/libsysprof-ui/sysprof-profiler-assistant.h deleted file mode 100644 index ff06c78b..00000000 --- a/src/libsysprof-ui/sysprof-profiler-assistant.h +++ /dev/null @@ -1,37 +0,0 @@ -/* sysprof-profiler-assistant.h - * - * Copyright 2019 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_PROFILER_ASSISTANT (sysprof_profiler_assistant_get_type()) - -G_DECLARE_FINAL_TYPE (SysprofProfilerAssistant, sysprof_profiler_assistant, SYSPROF, PROFILER_ASSISTANT, GtkWidget) - -GtkWidget *sysprof_profiler_assistant_new (void); -void sysprof_profiler_assistant_set_executable (SysprofProfilerAssistant *self, - const gchar *path); - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-profiler-assistant.ui b/src/libsysprof-ui/sysprof-profiler-assistant.ui deleted file mode 100644 index 669c21ab..00000000 --- a/src/libsysprof-ui/sysprof-profiler-assistant.ui +++ /dev/null @@ -1,279 +0,0 @@ - - - - sysprof-cpu - - - sysprof-memory - - - sysprof-allocations - - - sysprof-calgraph - - - sysprof-networking - - - sysprof-rapl - - - /org/gnome/Sysprof3/Profiler - session - org.gnome.Shell - - GNOME Shell - sysprof-library - - - Speedtrack - sysprof-gtk - - - libsysprof-speedtrack-4.so - - - - - GJS - sysprof-cli - - - - - - Application - sysprof-trace-app - - - SYSPROF_TRACE_FD - - - - - sysprof-battery - - - Disk - sysprof-disk - - - - - - diff --git a/src/libsysprof-ui/sysprof-proxy-aid.c b/src/libsysprof-ui/sysprof-proxy-aid.c deleted file mode 100644 index 6fd05c0e..00000000 --- a/src/libsysprof-ui/sysprof-proxy-aid.c +++ /dev/null @@ -1,209 +0,0 @@ -/* sysprof-proxy-aid.c - * - * Copyright 2019 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 - */ - -#define G_LOG_DOMAIN "sysprof-proxy-aid" - -#include "sysprof-proxy-aid.h" - -typedef struct -{ - GBusType bus_type; - gchar *bus_name; - gchar *object_path; -} SysprofProxyAidPrivate; - -enum { - PROP_0, - PROP_BUS_TYPE, - PROP_BUS_NAME, - PROP_OBJECT_PATH, - N_PROPS -}; - -G_DEFINE_TYPE_WITH_PRIVATE (SysprofProxyAid, sysprof_proxy_aid, SYSPROF_TYPE_AID) - -static GParamSpec *properties [N_PROPS]; - -static void -sysprof_proxy_aid_prepare (SysprofAid *aid, - SysprofProfiler *profiler) -{ - SysprofProxyAid *self = (SysprofProxyAid *)aid; - SysprofProxyAidPrivate *priv = sysprof_proxy_aid_get_instance_private (self); - g_autoptr(SysprofSource) source = NULL; - - g_assert (SYSPROF_IS_PROXY_AID (self)); - g_assert (SYSPROF_IS_PROFILER (profiler)); - - source = sysprof_proxy_source_new (priv->bus_type, priv->bus_name, priv->object_path); - sysprof_profiler_add_source (profiler, source); -} - -static void -sysprof_proxy_aid_finalize (GObject *object) -{ - SysprofProxyAid *self = (SysprofProxyAid *)object; - SysprofProxyAidPrivate *priv = sysprof_proxy_aid_get_instance_private (self); - - g_clear_pointer (&priv->bus_name, g_free); - g_clear_pointer (&priv->object_path, g_free); - - G_OBJECT_CLASS (sysprof_proxy_aid_parent_class)->finalize (object); -} - -static void -sysprof_proxy_aid_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - SysprofProxyAid *self = SYSPROF_PROXY_AID (object); - SysprofProxyAidPrivate *priv = sysprof_proxy_aid_get_instance_private (self); - - switch (prop_id) - { - case PROP_BUS_NAME: - g_value_set_string (value, priv->bus_name); - break; - - case PROP_OBJECT_PATH: - g_value_set_string (value, priv->object_path); - break; - - case PROP_BUS_TYPE: - g_value_set_enum (value, priv->bus_type); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_proxy_aid_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - SysprofProxyAid *self = SYSPROF_PROXY_AID (object); - - switch (prop_id) - { - case PROP_BUS_NAME: - sysprof_proxy_aid_set_bus_name (self, g_value_get_string (value)); - break; - - case PROP_BUS_TYPE: - sysprof_proxy_aid_set_bus_type (self, g_value_get_enum (value)); - break; - - case PROP_OBJECT_PATH: - sysprof_proxy_aid_set_object_path (self, g_value_get_string (value)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_proxy_aid_class_init (SysprofProxyAidClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - SysprofAidClass *aid_class = SYSPROF_AID_CLASS (klass); - - object_class->finalize = sysprof_proxy_aid_finalize; - object_class->get_property = sysprof_proxy_aid_get_property; - object_class->set_property = sysprof_proxy_aid_set_property; - - aid_class->prepare = sysprof_proxy_aid_prepare; - - properties [PROP_OBJECT_PATH] = - g_param_spec_string ("object-path", NULL, NULL, - NULL, - (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - properties [PROP_BUS_NAME] = - g_param_spec_string ("bus-name", NULL, NULL, - NULL, - (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - properties [PROP_BUS_TYPE] = - g_param_spec_enum ("bus-type", NULL, NULL, - G_TYPE_BUS_TYPE, - G_BUS_TYPE_SESSION, - (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_properties (object_class, N_PROPS, properties); -} - -static void -sysprof_proxy_aid_init (SysprofProxyAid *self) -{ - SysprofProxyAidPrivate *priv = sysprof_proxy_aid_get_instance_private (self); - - priv->bus_type = G_BUS_TYPE_SESSION; - priv->object_path = g_strdup ("/org/gnome/Sysprof3/Profiler"); -} - -void -sysprof_proxy_aid_set_bus_type (SysprofProxyAid *self, - GBusType bus_type) -{ - SysprofProxyAidPrivate *priv = sysprof_proxy_aid_get_instance_private (self); - - g_return_if_fail (SYSPROF_IS_PROXY_AID (self)); - g_return_if_fail (bus_type == G_BUS_TYPE_SESSION || bus_type == G_BUS_TYPE_SYSTEM); - - priv->bus_type = bus_type; - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_BUS_TYPE]); -} - -void -sysprof_proxy_aid_set_bus_name (SysprofProxyAid *self, - const gchar *bus_name) -{ - SysprofProxyAidPrivate *priv = sysprof_proxy_aid_get_instance_private (self); - - g_return_if_fail (SYSPROF_IS_PROXY_AID (self)); - - if (g_strcmp0 (bus_name, priv->bus_name) != 0) - { - g_free (priv->bus_name); - priv->bus_name = g_strdup (bus_name); - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_BUS_NAME]); - } -} - -void -sysprof_proxy_aid_set_object_path (SysprofProxyAid *self, - const gchar *object_path) -{ - SysprofProxyAidPrivate *priv = sysprof_proxy_aid_get_instance_private (self); - - g_return_if_fail (SYSPROF_IS_PROXY_AID (self)); - - if (g_strcmp0 (object_path, priv->object_path) != 0) - { - g_free (priv->object_path); - priv->object_path = g_strdup (object_path); - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_OBJECT_PATH]); - } -} diff --git a/src/libsysprof-ui/sysprof-proxy-aid.h b/src/libsysprof-ui/sysprof-proxy-aid.h deleted file mode 100644 index 218ba117..00000000 --- a/src/libsysprof-ui/sysprof-proxy-aid.h +++ /dev/null @@ -1,46 +0,0 @@ -/* sysprof-proxy-aid.h - * - * Copyright 2019 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-aid.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_PROXY_AID (sysprof_proxy_aid_get_type()) - -G_DECLARE_DERIVABLE_TYPE (SysprofProxyAid, sysprof_proxy_aid, SYSPROF, PROXY_AID, SysprofAid) - -struct _SysprofProxyAidClass -{ - SysprofAidClass parent_class; - - /*< private >*/ - gpointer _reserved[8]; -}; - -void sysprof_proxy_aid_set_bus_type (SysprofProxyAid *self, - GBusType bus_type); -void sysprof_proxy_aid_set_bus_name (SysprofProxyAid *self, - const gchar *bus_name); -void sysprof_proxy_aid_set_object_path (SysprofProxyAid *self, - const gchar *obj_path); - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-rapl-aid.c b/src/libsysprof-ui/sysprof-rapl-aid.c deleted file mode 100644 index 9e2c88ee..00000000 --- a/src/libsysprof-ui/sysprof-rapl-aid.c +++ /dev/null @@ -1,253 +0,0 @@ -/* sysprof-rapl-aid.c - * - * Copyright 2019 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 - */ - -#define G_LOG_DOMAIN "sysprof-rapl-aid" - -#include "config.h" - -#include - -#include "sysprof-color-cycle.h" -#include "sysprof-rapl-aid.h" -#include "sysprof-line-visualizer.h" -#include "sysprof-proxy-aid.h" - -struct _SysprofRaplAid -{ - SysprofProxyAid parent_instance; -}; - -typedef struct -{ - SysprofCaptureCursor *cursor; - SysprofDisplay *display; - GArray *counters; -} Present; - -G_DEFINE_TYPE (SysprofRaplAid, sysprof_rapl_aid, SYSPROF_TYPE_PROXY_AID) - -static void -present_free (gpointer data) -{ - Present *p = data; - - g_clear_pointer (&p->cursor, sysprof_capture_cursor_unref); - g_clear_pointer (&p->counters, g_array_unref); - g_clear_object (&p->display); - g_slice_free (Present, p); -} - -/** - * sysprof_rapl_aid_new: - * - * Create a new #SysprofRaplAid. - * - * Returns: (transfer full): a newly created #SysprofRaplAid - * - * Since: 3.34 - */ -SysprofAid * -sysprof_rapl_aid_new (void) -{ - return g_object_new (SYSPROF_TYPE_RAPL_AID, NULL); -} - -static bool -collect_info (const SysprofCaptureFrame *frame, - gpointer user_data) -{ - SysprofCaptureCounterDefine *def = (SysprofCaptureCounterDefine *)frame; - Present *p = user_data; - - g_assert (frame != NULL); - g_assert (p != NULL); - g_assert (p->counters != NULL); - - if (frame->type == SYSPROF_CAPTURE_FRAME_CTRDEF) - { - for (guint i = 0; i < def->n_counters; i++) - { - const SysprofCaptureCounter *counter = &def->counters[i]; - - if (g_str_has_prefix (counter->category, "RAPL")) - g_array_append_vals (p->counters, counter, 1); - } - } - - return TRUE; -} - -static void -sysprof_rapl_aid_present_worker (GTask *task, - gpointer source_object, - gpointer task_data, - GCancellable *cancellable) -{ - Present *present = task_data; - - g_assert (G_IS_TASK (task)); - g_assert (SYSPROF_IS_RAPL_AID (source_object)); - g_assert (present != NULL); - g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); - - sysprof_capture_cursor_foreach (present->cursor, collect_info, present); - g_task_return_pointer (task, - g_steal_pointer (&present->counters), - (GDestroyNotify) g_array_unref); -} - -static void -sysprof_rapl_aid_present_async (SysprofAid *aid, - SysprofCaptureReader *reader, - SysprofDisplay *display, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - static const SysprofCaptureFrameType types[] = { SYSPROF_CAPTURE_FRAME_CTRDEF, }; - g_autoptr(SysprofCaptureCondition) condition = NULL; - g_autoptr(SysprofCaptureCursor) cursor = NULL; - g_autoptr(GTask) task = NULL; - Present present; - - g_assert (SYSPROF_IS_RAPL_AID (aid)); - g_assert (reader != NULL); - g_assert (SYSPROF_IS_DISPLAY (display)); - g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); - - condition = sysprof_capture_condition_new_where_type_in (G_N_ELEMENTS (types), types); - cursor = sysprof_capture_cursor_new (reader); - sysprof_capture_cursor_add_condition (cursor, g_steal_pointer (&condition)); - - present.cursor = g_steal_pointer (&cursor); - present.display = g_object_ref (display); - present.counters = g_array_new (FALSE, FALSE, sizeof (SysprofCaptureCounter)); - - task = g_task_new (aid, cancellable, callback, user_data); - g_task_set_source_tag (task, sysprof_rapl_aid_present_async); - g_task_set_task_data (task, - g_slice_dup (Present, &present), - present_free); - g_task_run_in_thread (task, sysprof_rapl_aid_present_worker); -} - -static gboolean -sysprof_rapl_aid_present_finish (SysprofAid *aid, - GAsyncResult *result, - GError **error) -{ - g_autoptr(GArray) counters = NULL; - Present *present; - - g_assert (SYSPROF_IS_AID (aid)); - g_assert (G_IS_TASK (result)); - - present = g_task_get_task_data (G_TASK (result)); - - if ((counters = g_task_propagate_pointer (G_TASK (result), error)) && counters->len) - { - g_autoptr(SysprofColorCycle) cycle = sysprof_color_cycle_new (); - g_autoptr(GHashTable) cat_to_row = g_hash_table_new (g_str_hash, g_str_equal); - SysprofVisualizerGroup *energy; - SysprofVisualizer *all; - guint found = 0; - - energy = g_object_new (SYSPROF_TYPE_VISUALIZER_GROUP, - "can-focus", TRUE, - "priority", -300, - "title", _("Energy Usage"), - "visible", TRUE, - NULL); - - all = g_object_new (SYSPROF_TYPE_LINE_VISUALIZER, - "title", _("Energy Usage (All)"), - "height-request", 35, - "visible", TRUE, - "y-lower", 0.0, - "units", "Watts", - NULL); - sysprof_visualizer_group_insert (energy, SYSPROF_VISUALIZER (all), 0, FALSE); - - for (guint i = 0; i < counters->len; i++) - { - const SysprofCaptureCounter *ctr = &g_array_index (counters, SysprofCaptureCounter, i); - - /* The pseudo counters (core:-1 cpu:-1) have "RAPL" as the group */ - if (g_strcmp0 (ctr->category, "RAPL") == 0) - { - GdkRGBA rgba; - - sysprof_color_cycle_next (cycle, &rgba); - sysprof_line_visualizer_add_counter (SYSPROF_LINE_VISUALIZER (all), ctr->id, &rgba); - found++; - } - else if (g_str_has_prefix (ctr->category, "RAPL ")) - { - SysprofVisualizer *row; - GdkRGBA rgba; - - row = g_hash_table_lookup (cat_to_row, ctr->category); - - if (row == NULL) - { - row = g_object_new (SYSPROF_TYPE_LINE_VISUALIZER, - "title", ctr->category, - "height-request", 20, - "visible", FALSE, - "y-lower", 0.0, - "units", "Watts", - NULL); - g_hash_table_insert (cat_to_row, (gchar *)ctr->category, row); - sysprof_visualizer_group_insert (energy, SYSPROF_VISUALIZER (row), -1, TRUE); - } - - sysprof_color_cycle_next (cycle, &rgba); - sysprof_line_visualizer_add_counter (SYSPROF_LINE_VISUALIZER (row), ctr->id, &rgba); - found++; - } - } - - if (found > 0) - sysprof_display_add_group (present->display, energy); - else - g_object_unref (g_object_ref_sink (energy)); - } - - return counters != NULL; -} - -static void -sysprof_rapl_aid_class_init (SysprofRaplAidClass *klass) -{ - SysprofAidClass *aid_class = SYSPROF_AID_CLASS (klass); - - aid_class->present_async = sysprof_rapl_aid_present_async; - aid_class->present_finish = sysprof_rapl_aid_present_finish; -} - -static void -sysprof_rapl_aid_init (SysprofRaplAid *self) -{ - sysprof_aid_set_display_name (SYSPROF_AID (self), _("Energy Usage")); - sysprof_aid_set_icon_name (SYSPROF_AID (self), "battery-low-charging-symbolic"); - sysprof_proxy_aid_set_object_path (SYSPROF_PROXY_AID (self), "/org/gnome/Sysprof3/RAPL"); - sysprof_proxy_aid_set_bus_type (SYSPROF_PROXY_AID (self), G_BUS_TYPE_SYSTEM); - sysprof_proxy_aid_set_bus_name (SYSPROF_PROXY_AID (self), "org.gnome.Sysprof3"); -} diff --git a/src/libsysprof-ui/sysprof-rapl-aid.h b/src/libsysprof-ui/sysprof-rapl-aid.h deleted file mode 100644 index 8721c5dd..00000000 --- a/src/libsysprof-ui/sysprof-rapl-aid.h +++ /dev/null @@ -1,33 +0,0 @@ -/* sysprof-rapl-aid.h - * - * Copyright 2019 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-proxy-aid.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_RAPL_AID (sysprof_rapl_aid_get_type()) - -G_DECLARE_FINAL_TYPE (SysprofRaplAid, sysprof_rapl_aid, SYSPROF, RAPL_AID, SysprofProxyAid) - -SysprofAid *sysprof_rapl_aid_new (void); - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-recording-state-view.c b/src/libsysprof-ui/sysprof-recording-state-view.c deleted file mode 100644 index ea025a4b..00000000 --- a/src/libsysprof-ui/sysprof-recording-state-view.c +++ /dev/null @@ -1,202 +0,0 @@ -/* sysprof-recording-state-view.c - * - * Copyright 2016-2019 Christian Hergert - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -#include "config.h" - -#include "sysprof-recording-state-view.h" -#include "sysprof-time-label.h" - -typedef struct -{ - SysprofProfiler *profiler; - SysprofTimeLabel *elapsed; - GtkLabel *samples; - gulong notify_elapsed_handler; -} SysprofRecordingStateViewPrivate; - -G_DEFINE_TYPE_WITH_PRIVATE (SysprofRecordingStateView, sysprof_recording_state_view, GTK_TYPE_WIDGET) - -enum { - PROP_0, - PROP_PROFILER, - N_PROPS -}; - -static GParamSpec *properties [N_PROPS]; - -GtkWidget * -sysprof_recording_state_view_new (void) -{ - return g_object_new (SYSPROF_TYPE_RECORDING_STATE_VIEW, NULL); -} - -static void -sysprof_recording_state_view_notify_elapsed (SysprofRecordingStateView *self, - GParamSpec *pspec, - SysprofProfiler *profiler) -{ - SysprofRecordingStateViewPrivate *priv = sysprof_recording_state_view_get_instance_private (self); - SysprofCaptureWriter *writer; - gint64 elapsed; - - g_assert (SYSPROF_IS_RECORDING_STATE_VIEW (self)); - g_assert (SYSPROF_IS_PROFILER (profiler)); - - if ((writer = sysprof_profiler_get_writer (profiler))) - { - SysprofCaptureStat st; - g_autofree gchar *samples = NULL; - gint64 count; - - sysprof_capture_writer_stat (writer, &st); - count = st.frame_count[SYSPROF_CAPTURE_FRAME_SAMPLE] + - st.frame_count[SYSPROF_CAPTURE_FRAME_MARK] + - st.frame_count[SYSPROF_CAPTURE_FRAME_CTRSET]; - - samples = g_strdup_printf ("%"G_GINT64_FORMAT, count); - gtk_label_set_label (priv->samples, samples); - } - - elapsed = (gint64)sysprof_profiler_get_elapsed (profiler); - sysprof_time_label_set_duration (priv->elapsed, elapsed); -} - -static void -sysprof_recording_state_view_dispose (GObject *object) -{ - SysprofRecordingStateView *self = (SysprofRecordingStateView *)object; - SysprofRecordingStateViewPrivate *priv = sysprof_recording_state_view_get_instance_private (self); - GtkWidget *child; - - while ((child = gtk_widget_get_first_child (GTK_WIDGET (self)))) - gtk_widget_unparent (child); - - if (priv->profiler != NULL) - { - g_clear_signal_handler (&priv->notify_elapsed_handler, priv->profiler); - g_clear_object (&priv->profiler); - } - - G_OBJECT_CLASS (sysprof_recording_state_view_parent_class)->dispose (object); -} - -static void -sysprof_recording_state_view_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - SysprofRecordingStateView *self = SYSPROF_RECORDING_STATE_VIEW (object); - SysprofRecordingStateViewPrivate *priv = sysprof_recording_state_view_get_instance_private (self); - - switch (prop_id) - { - case PROP_PROFILER: - g_value_set_object (value, priv->profiler); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_recording_state_view_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - SysprofRecordingStateView *self = SYSPROF_RECORDING_STATE_VIEW (object); - - switch (prop_id) - { - case PROP_PROFILER: - sysprof_recording_state_view_set_profiler (self, g_value_get_object (value)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_recording_state_view_class_init (SysprofRecordingStateViewClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); - - object_class->dispose = sysprof_recording_state_view_dispose; - object_class->get_property = sysprof_recording_state_view_get_property; - object_class->set_property = sysprof_recording_state_view_set_property; - - properties [PROP_PROFILER] = - g_param_spec_object ("profiler", - "Profiler", - "Profiler", - SYSPROF_TYPE_PROFILER, - (G_PARAM_READWRITE | 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/ui/sysprof-recording-state-view.ui"); - gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT); - gtk_widget_class_bind_template_child_private (widget_class, SysprofRecordingStateView, elapsed); - gtk_widget_class_bind_template_child_private (widget_class, SysprofRecordingStateView, samples); - - g_type_ensure (SYSPROF_TYPE_TIME_LABEL); -} - -static void -sysprof_recording_state_view_init (SysprofRecordingStateView *self) -{ - gtk_widget_init_template (GTK_WIDGET (self)); -} - -void -sysprof_recording_state_view_set_profiler (SysprofRecordingStateView *self, - SysprofProfiler *profiler) -{ - SysprofRecordingStateViewPrivate *priv = sysprof_recording_state_view_get_instance_private (self); - - g_assert (SYSPROF_IS_RECORDING_STATE_VIEW (self)); - g_assert (!profiler || SYSPROF_IS_PROFILER (profiler)); - - sysprof_time_label_set_duration (priv->elapsed, 0); - - if (profiler != priv->profiler) - { - if (priv->profiler != NULL) - { - g_signal_handler_disconnect (priv->profiler, priv->notify_elapsed_handler); - g_clear_object (&priv->profiler); - } - - if (profiler != NULL) - { - priv->profiler = g_object_ref (profiler); - priv->notify_elapsed_handler = - g_signal_connect_object (profiler, - "notify::elapsed", - G_CALLBACK (sysprof_recording_state_view_notify_elapsed), - self, - G_CONNECT_SWAPPED); - } - } -} diff --git a/src/libsysprof-ui/sysprof-recording-state-view.h b/src/libsysprof-ui/sysprof-recording-state-view.h deleted file mode 100644 index b53481f4..00000000 --- a/src/libsysprof-ui/sysprof-recording-state-view.h +++ /dev/null @@ -1,41 +0,0 @@ -/* sysprof-recording-state-view.h - * - * Copyright 2016-2019 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 - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_RECORDING_STATE_VIEW (sysprof_recording_state_view_get_type()) - -G_DECLARE_DERIVABLE_TYPE (SysprofRecordingStateView, sysprof_recording_state_view, SYSPROF, RECORDING_STATE_VIEW, GtkWidget) - -struct _SysprofRecordingStateViewClass -{ - GtkWidgetClass parent; -}; - -GtkWidget *sysprof_recording_state_view_new (void); -void sysprof_recording_state_view_set_profiler (SysprofRecordingStateView *self, - SysprofProfiler *profiler); - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-recording-state-view.ui b/src/libsysprof-ui/sysprof-recording-state-view.ui deleted file mode 100644 index 1885f885..00000000 --- a/src/libsysprof-ui/sysprof-recording-state-view.ui +++ /dev/null @@ -1,83 +0,0 @@ - - - - diff --git a/src/libsysprof-ui/sysprof-scrollmap.c b/src/libsysprof-ui/sysprof-scrollmap.c deleted file mode 100644 index 6dbddbb9..00000000 --- a/src/libsysprof-ui/sysprof-scrollmap.c +++ /dev/null @@ -1,333 +0,0 @@ -/* sysprof-scrollmap.c - * - * Copyright 2019 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 - */ - -#define G_LOG_DOMAIN "sysprof-scrollmap" - -#include "config.h" - -#include "sysprof-scrollmap.h" - -#define BOX_SIZE 4 - -struct _SysprofScrollmap -{ - GtkWidget parent_instance; - - GtkWidget *scrollbar; - - gint64 begin_time; - gint64 end_time; - - GArray *timings; - GArray *buckets; - GCancellable *cancellable; - - gint most; -}; - -typedef struct -{ - gint64 begin_time; - gint64 end_time; - GArray *timings; - gint width; - gint height; -} Recalculate; - -G_DEFINE_TYPE (SysprofScrollmap, sysprof_scrollmap, GTK_TYPE_WIDGET) - -static void -recalculate_free (gpointer data) -{ - Recalculate *state = data; - - g_clear_pointer (&state->timings, g_array_unref); - g_slice_free (Recalculate, state); -} - -static void -sysprof_scrollmap_recalculate_worker (GTask *task, - gpointer source_object, - gpointer task_data, - GCancellable *cancellable) -{ - Recalculate *state = task_data; - g_autoptr(GArray) buckets = NULL; - gint64 duration; - gint n_buckets; - - g_assert (G_IS_TASK (task)); - g_assert (SYSPROF_IS_SCROLLMAP (source_object)); - g_assert (state != NULL); - g_assert (state->timings != NULL); - g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); - - duration = state->end_time - state->begin_time; - n_buckets = MAX (10, state->width / (BOX_SIZE + 1)); - buckets = g_array_sized_new (FALSE, TRUE, sizeof (gint), n_buckets); - g_array_set_size (buckets, n_buckets); - - for (guint i = 0; i < state->timings->len; i++) - { - gint64 t = g_array_index (state->timings, gint64, i); - gint n; - - if (t < state->begin_time || t > state->end_time) - continue; - - n = MIN (n_buckets - 1, ((t - state->begin_time) / (gdouble)duration) * n_buckets); - - g_assert (n < n_buckets); - - g_array_index (buckets, gint, n)++; - } - - g_task_return_pointer (task, - g_steal_pointer (&buckets), - (GDestroyNotify) g_array_unref); -} - -static void -sysprof_scrollmap_recalculate_async (SysprofScrollmap *self, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - g_autoptr(GTask) task = NULL; - GtkAllocation alloc; - Recalculate state; - - g_assert (SYSPROF_IS_SCROLLMAP (self)); - g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); - - task = g_task_new (self, cancellable, callback, user_data); - g_task_set_source_tag (task, sysprof_scrollmap_recalculate_async); - - if (self->timings == NULL) - { - g_task_return_new_error (task, - G_IO_ERROR, - G_IO_ERROR_CANCELLED, - "The operation was cancelled"); - return; - } - - gtk_widget_get_allocation (GTK_WIDGET (self), &alloc); - - state.begin_time = self->begin_time; - state.end_time = self->end_time; - state.width = alloc.width; - state.height = alloc.height; - state.timings = g_array_ref (self->timings); - - g_task_set_task_data (task, - g_slice_dup (Recalculate, &state), - recalculate_free); - g_task_run_in_thread (task, sysprof_scrollmap_recalculate_worker); -} - -static GArray * -sysprof_scrollmap_recalculate_finish (SysprofScrollmap *self, - GAsyncResult *result, - GError **error) -{ - g_assert (SYSPROF_IS_SCROLLMAP (self)); - g_assert (G_IS_TASK (result)); - - return g_task_propagate_pointer (G_TASK (result), error); -} - -static inline void -draw_boxes (const GtkAllocation *alloc, - GtkSnapshot *snapshot, - int x, - int n_boxes, - const GdkRGBA *color) -{ - int y = alloc->y + alloc->height - BOX_SIZE; - - for (int i = 0; i < n_boxes; i++) - { - gtk_snapshot_append_color (snapshot, color, &GRAPHENE_RECT_INIT (x, y, BOX_SIZE, -BOX_SIZE)); - y -= (BOX_SIZE + 1); - } -} - -static void -sysprof_scrollmap_snapshot (GtkWidget *widget, - GtkSnapshot *snapshot) -{ - SysprofScrollmap *self = (SysprofScrollmap *)widget; - GtkStyleContext *style_context; - GtkAllocation alloc; - GdkRGBA color; - int max_boxes; - - g_assert (SYSPROF_IS_SCROLLMAP (self)); - g_assert (GTK_IS_SNAPSHOT (snapshot)); - - if (self->buckets == NULL) - goto chainup; - - gtk_widget_get_allocation (widget, &alloc); - - alloc.y += 3; - alloc.height -= 6; - - max_boxes = alloc.height / (BOX_SIZE + 1) - 1; - - style_context = gtk_widget_get_style_context (widget); - gtk_style_context_get_color (style_context, &color); - - for (guint i = 0; i < self->buckets->len; i++) - { - int n = g_array_index (self->buckets, gint, i); - int x = 1 + i * (BOX_SIZE + 1); - int b = max_boxes * (n / (gdouble)self->most); - - if (n > 0) - b = MAX (b, 1); - - draw_boxes (&alloc, snapshot, x, b, &color); - } - -chainup: - GTK_WIDGET_CLASS (sysprof_scrollmap_parent_class)->snapshot (widget, snapshot); -} - -static void -sysprof_scrollmap_recalculate_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - SysprofScrollmap *self = (SysprofScrollmap *)object; - g_autoptr(GArray) buckets = NULL; - - g_assert (SYSPROF_IS_SCROLLMAP (self)); - g_assert (G_IS_ASYNC_RESULT (result)); - g_assert (user_data == NULL); - - if ((buckets = sysprof_scrollmap_recalculate_finish (self, result, NULL))) - { - self->most = 0; - - for (guint i = 0; i < buckets->len; i++) - { - gint n = g_array_index (buckets, gint, i); - self->most = MAX (self->most, n); - } - - g_clear_pointer (&self->buckets, g_array_unref); - self->buckets = g_steal_pointer (&buckets); - - gtk_widget_queue_draw (GTK_WIDGET (self)); - } -} - -static void -sysprof_scrollmap_dispose (GObject *object) -{ - SysprofScrollmap *self = (SysprofScrollmap *)object; - - if (self->scrollbar) - { - gtk_widget_unparent (GTK_WIDGET (self->scrollbar)); - self->scrollbar = NULL; - } - - g_clear_pointer (&self->buckets, g_array_unref); - g_clear_pointer (&self->timings, g_array_unref); - - G_OBJECT_CLASS (sysprof_scrollmap_parent_class)->dispose (object); -} - -static void -sysprof_scrollmap_class_init (SysprofScrollmapClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); - - object_class->dispose = sysprof_scrollmap_dispose; - - widget_class->snapshot = sysprof_scrollmap_snapshot; - - gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT); - gtk_widget_class_set_css_name (widget_class, "scrollmap"); -} - -static void -sysprof_scrollmap_init (SysprofScrollmap *self) -{ - self->scrollbar = g_object_new (GTK_TYPE_SCROLLBAR, - "orientation", GTK_ORIENTATION_HORIZONTAL, - NULL); - gtk_widget_set_parent (GTK_WIDGET (self->scrollbar), GTK_WIDGET (self)); -} - -void -sysprof_scrollmap_set_timings (SysprofScrollmap *self, - GArray *timings) -{ - g_return_if_fail (SYSPROF_IS_SCROLLMAP (self)); - - if (timings != self->timings) - { - g_clear_pointer (&self->timings, g_array_unref); - self->timings = timings ? g_array_ref (timings) : NULL; - } -} - -void -sysprof_scrollmap_set_time_range (SysprofScrollmap *self, - gint64 begin_time, - gint64 end_time) -{ - g_return_if_fail (SYSPROF_IS_SCROLLMAP (self)); - - self->begin_time = begin_time; - self->end_time = end_time; - - g_cancellable_cancel (self->cancellable); - g_clear_object (&self->cancellable); - self->cancellable = g_cancellable_new (); - - sysprof_scrollmap_recalculate_async (self, - self->cancellable, - sysprof_scrollmap_recalculate_cb, - NULL); -} - -void -sysprof_scrollmap_set_adjustment (SysprofScrollmap *self, - GtkAdjustment *adjustment) -{ - g_return_if_fail (SYSPROF_IS_SCROLLMAP (self)); - g_return_if_fail (!adjustment || GTK_IS_ADJUSTMENT (adjustment)); - - gtk_scrollbar_set_adjustment (GTK_SCROLLBAR (self->scrollbar), adjustment); -} - -GtkAdjustment * -sysprof_scrollmap_get_adjustment (SysprofScrollmap *self) -{ - g_return_val_if_fail (SYSPROF_IS_SCROLLMAP (self), NULL); - - return gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (self->scrollbar)); -} diff --git a/src/libsysprof-ui/sysprof-scrollmap.h b/src/libsysprof-ui/sysprof-scrollmap.h deleted file mode 100644 index 2fbe1721..00000000 --- a/src/libsysprof-ui/sysprof-scrollmap.h +++ /dev/null @@ -1,40 +0,0 @@ -/* sysprof-scrollmap.h - * - * Copyright 2019 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_SCROLLMAP (sysprof_scrollmap_get_type()) - -G_DECLARE_FINAL_TYPE (SysprofScrollmap, sysprof_scrollmap, SYSPROF, SCROLLMAP, GtkWidget) - -GtkAdjustment *sysprof_scrollmap_get_adjustment (SysprofScrollmap *self); -void sysprof_scrollmap_set_adjustment (SysprofScrollmap *self, - GtkAdjustment *adjustment); -void sysprof_scrollmap_set_timings (SysprofScrollmap *self, - GArray *timings); -void sysprof_scrollmap_set_time_range (SysprofScrollmap *self, - gint64 begin_time, - gint64 end_time); - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-tab.c b/src/libsysprof-ui/sysprof-tab.c deleted file mode 100644 index 8554cc71..00000000 --- a/src/libsysprof-ui/sysprof-tab.c +++ /dev/null @@ -1,158 +0,0 @@ -/* sysprof-tab.c - * - * Copyright 2019 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 - */ - -#define G_LOG_DOMAIN "sysprof-tab" - -#include "config.h" - -#include "sysprof-display-private.h" -#include "sysprof-tab.h" -#include "sysprof-ui-private.h" - -struct _SysprofTab -{ - GtkWidget parent_instance; - - GtkWidget *center_box; - GtkButton *close_button; - GtkLabel *title; - GtkImage *recording; - - SysprofDisplay *display; -}; - -G_DEFINE_TYPE (SysprofTab, sysprof_tab, GTK_TYPE_WIDGET) - -enum { - PROP_0, - PROP_DISPLAY, - N_PROPS -}; - -static GParamSpec *properties [N_PROPS]; - -GtkWidget * -sysprof_tab_new (SysprofDisplay *display) -{ - return g_object_new (SYSPROF_TYPE_TAB, - "display", display, - NULL); -} - -static void -sysprof_tab_close_clicked (SysprofTab *self, - GtkButton *button) -{ - g_assert (SYSPROF_IS_TAB (self)); - g_assert (GTK_IS_BUTTON (button)); - - if (self->display) - _sysprof_display_destroy (self->display); -} - -static void -sysprof_tab_dispose (GObject *object) -{ - SysprofTab *self = (SysprofTab *)object; - - g_clear_pointer (&self->center_box, gtk_widget_unparent); - g_clear_weak_pointer (&self->display); - - G_OBJECT_CLASS (sysprof_tab_parent_class)->dispose (object); -} - -static void -sysprof_tab_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - SysprofTab *self = SYSPROF_TAB (object); - - switch (prop_id) - { - case PROP_DISPLAY: - g_value_set_object (value, self->display); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_tab_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - SysprofTab *self = SYSPROF_TAB (object); - - switch (prop_id) - { - case PROP_DISPLAY: - g_set_weak_pointer (&self->display, g_value_get_object (value)); - g_object_bind_property (self->display, "title", self->title, "label", G_BINDING_SYNC_CREATE); - g_object_bind_property (self->display, "recording", self->recording, "visible", G_BINDING_SYNC_CREATE); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_tab_class_init (SysprofTabClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); - - object_class->dispose = sysprof_tab_dispose; - object_class->get_property = sysprof_tab_get_property; - object_class->set_property = sysprof_tab_set_property; - - gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/sysprof/ui/sysprof-tab.ui"); - gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT); - gtk_widget_class_bind_template_child (widget_class, SysprofTab, center_box); - gtk_widget_class_bind_template_child (widget_class, SysprofTab, close_button); - gtk_widget_class_bind_template_child (widget_class, SysprofTab, recording); - gtk_widget_class_bind_template_child (widget_class, SysprofTab, title); - - properties [PROP_DISPLAY] = - g_param_spec_object ("display", - "Display", - "The display widget for the tab", - SYSPROF_TYPE_DISPLAY, - (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_properties (object_class, N_PROPS, properties); -} - -static void -sysprof_tab_init (SysprofTab *self) -{ - gtk_widget_init_template (GTK_WIDGET (self)); - - g_signal_connect_object (self->close_button, - "clicked", - G_CALLBACK (sysprof_tab_close_clicked), - self, - G_CONNECT_SWAPPED); -} diff --git a/src/libsysprof-ui/sysprof-tab.h b/src/libsysprof-ui/sysprof-tab.h deleted file mode 100644 index 5aff386b..00000000 --- a/src/libsysprof-ui/sysprof-tab.h +++ /dev/null @@ -1,35 +0,0 @@ -/* sysprof-tab.h - * - * Copyright 2019 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-display.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_TAB (sysprof_tab_get_type()) - -G_DECLARE_FINAL_TYPE (SysprofTab, sysprof_tab, SYSPROF, TAB, GtkWidget) - -GtkWidget *sysprof_tab_new (SysprofDisplay *display); - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-tab.ui b/src/libsysprof-ui/sysprof-tab.ui deleted file mode 100644 index f57f717d..00000000 --- a/src/libsysprof-ui/sysprof-tab.ui +++ /dev/null @@ -1,36 +0,0 @@ - - - - diff --git a/src/libsysprof-ui/sysprof-theme-manager.c b/src/libsysprof-ui/sysprof-theme-manager.c deleted file mode 100644 index 3c07b27d..00000000 --- a/src/libsysprof-ui/sysprof-theme-manager.c +++ /dev/null @@ -1,269 +0,0 @@ -/* sysprof-theme-manager.c - * - * Copyright 2016-2019 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 - */ - -#define G_LOG_DOMAIN "sysprof-theme-manager" - -#include "config.h" - -#include "sysprof-theme-manager.h" - -struct _SysprofThemeManager -{ - GObject parent_instance; - GHashTable *theme_resources; - guint reload_source; - guint registered_signals : 1; -}; - -typedef struct -{ - guint id; - gchar *key; - gchar *theme_name; - gchar *variant; - gchar *resource; - GtkCssProvider *provider; -} ThemeResource; - -G_DEFINE_TYPE (SysprofThemeManager, sysprof_theme_manager, G_TYPE_OBJECT) - -static void -theme_resource_free (gpointer data) -{ - ThemeResource *theme_resource = data; - - if (theme_resource != NULL) - { - g_clear_pointer (&theme_resource->key, g_free); - g_clear_pointer (&theme_resource->theme_name, g_free); - g_clear_pointer (&theme_resource->variant, g_free); - g_clear_pointer (&theme_resource->resource, g_free); - - if (theme_resource->provider != NULL) - { - gtk_style_context_remove_provider_for_display (gdk_display_get_default (), - GTK_STYLE_PROVIDER (theme_resource->provider)); - g_clear_object (&theme_resource->provider); - } - - g_slice_free (ThemeResource, theme_resource); - } -} - -static gboolean -theme_resource_matches (ThemeResource *theme_resource, - GtkSettings *settings) -{ - g_autofree gchar *theme_name = NULL; - gboolean dark_theme = FALSE; - - g_assert (theme_resource != NULL); - g_assert (GTK_IS_SETTINGS (settings)); - - if (theme_resource->theme_name == NULL) - return TRUE; - - g_object_get (settings, - "gtk-theme-name", &theme_name, - "gtk-application-prefer-dark-theme", &dark_theme, - NULL); - - if (g_strcmp0 (theme_name, theme_resource->theme_name) == 0) - { - if (dark_theme && g_strcmp0 ("dark", theme_resource->variant) == 0) - return TRUE; - - if (!dark_theme && (!theme_resource->variant || g_strcmp0 ("light", theme_resource->variant) == 0)) - return TRUE; - } - - return FALSE; -} - -static gboolean -sysprof_theme_manager_do_reload (gpointer data) -{ - SysprofThemeManager *self = data; - ThemeResource *theme_resource; - GHashTableIter iter; - GtkSettings *settings; - - g_assert (SYSPROF_IS_THEME_MANAGER (self)); - - self->reload_source = 0; - - settings = gtk_settings_get_default (); - - g_hash_table_iter_init (&iter, self->theme_resources); - - while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&theme_resource)) - { - if (theme_resource_matches (theme_resource, settings)) - { - if (theme_resource->provider == NULL) - { - theme_resource->provider = gtk_css_provider_new (); - gtk_css_provider_load_from_resource (theme_resource->provider, theme_resource->resource); - gtk_style_context_add_provider_for_display (gdk_display_get_default (), - GTK_STYLE_PROVIDER (theme_resource->provider), - GTK_STYLE_PROVIDER_PRIORITY_THEME+1); - } - } - else - { - if (theme_resource->provider != NULL) - { - gtk_style_context_remove_provider_for_display (gdk_display_get_default (), - GTK_STYLE_PROVIDER (theme_resource->provider)); - g_clear_object (&theme_resource->provider); - } - } - } - - return G_SOURCE_REMOVE; -} - -static void -sysprof_theme_manager_queue_reload (SysprofThemeManager *self) -{ - g_assert (SYSPROF_IS_THEME_MANAGER (self)); - - if (self->reload_source == 0) - self->reload_source = g_idle_add_full (G_PRIORITY_LOW, - sysprof_theme_manager_do_reload, - self, - NULL); -} - -static void -sysprof_theme_manager_finalize (GObject *object) -{ - SysprofThemeManager *self = (SysprofThemeManager *)object; - - if (self->reload_source != 0) - { - g_source_remove (self->reload_source); - self->reload_source = 0; - } - - g_clear_pointer (&self->theme_resources, g_hash_table_unref); - - G_OBJECT_CLASS (sysprof_theme_manager_parent_class)->finalize (object); -} - -static void -sysprof_theme_manager_class_init (SysprofThemeManagerClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->finalize = sysprof_theme_manager_finalize; -} - -static void -sysprof_theme_manager_init (SysprofThemeManager *self) -{ - self->theme_resources = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, theme_resource_free); - - gtk_icon_theme_add_resource_path (gtk_icon_theme_get_for_display (gdk_display_get_default ()), - "/org/gnome/sysprof/icons"); -} - -/** - * sysprof_theme_manager_get_default: - * - * Returns: (transfer none): An #SysprofThemeManager - */ -SysprofThemeManager * -sysprof_theme_manager_get_default (void) -{ - static SysprofThemeManager *instance; - - if (instance == NULL) - instance = g_object_new (SYSPROF_TYPE_THEME_MANAGER, NULL); - - return instance; -} - -guint -sysprof_theme_manager_register_resource (SysprofThemeManager *self, - const gchar *theme_name, - const gchar *variant, - const gchar *resource) -{ - ThemeResource *theme_resource; - static guint counter; - guint id; - - g_return_val_if_fail (SYSPROF_IS_THEME_MANAGER (self), 0); - - theme_resource = g_slice_new0 (ThemeResource); - theme_resource->id = id = ++counter; - theme_resource->key = g_strdup_printf ("%s-%s-%d", - theme_name ? theme_name : "shared", - variant ? variant : "light", - theme_resource->id); - theme_resource->theme_name = g_strdup (theme_name); - theme_resource->variant = g_strdup (variant); - theme_resource->resource = g_strdup (resource); - theme_resource->provider = NULL; - - g_hash_table_insert (self->theme_resources, theme_resource->key, theme_resource); - - if (!self->registered_signals) - { - self->registered_signals = TRUE; - g_signal_connect_object (gtk_settings_get_default (), - "notify::gtk-application-prefer-dark-theme", - G_CALLBACK (sysprof_theme_manager_queue_reload), - self, - G_CONNECT_SWAPPED); - g_signal_connect_object (gtk_settings_get_default (), - "notify::gtk-theme-name", - G_CALLBACK (sysprof_theme_manager_queue_reload), - self, - G_CONNECT_SWAPPED); - } - - sysprof_theme_manager_queue_reload (self); - - return id; -} - -void -sysprof_theme_manager_unregister (SysprofThemeManager *self, - guint registration_id) -{ - GHashTableIter iter; - ThemeResource *theme_resource; - - g_return_if_fail (SYSPROF_IS_THEME_MANAGER (self)); - - g_hash_table_iter_init (&iter, self->theme_resources); - - while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&theme_resource)) - { - if (theme_resource->id == registration_id) - { - /* Provider is unregistered during destroy */ - g_hash_table_iter_remove (&iter); - break; - } - } -} diff --git a/src/libsysprof-ui/sysprof-theme-manager.h b/src/libsysprof-ui/sysprof-theme-manager.h deleted file mode 100644 index 3724628a..00000000 --- a/src/libsysprof-ui/sysprof-theme-manager.h +++ /dev/null @@ -1,47 +0,0 @@ -/* sysprof-theme-manager.h - * - * Copyright 2016-2019 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 - -#if !defined (SYSPROF_UI_INSIDE) && !defined (SYSPROF_UI_COMPILATION) -# error "Only can be included directly." -#endif - -#include - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_THEME_MANAGER (sysprof_theme_manager_get_type()) - -G_GNUC_INTERNAL -G_DECLARE_FINAL_TYPE (SysprofThemeManager, sysprof_theme_manager, SYSPROF, THEME_MANAGER, GObject) - -G_GNUC_INTERNAL -SysprofThemeManager *sysprof_theme_manager_get_default (void); -G_GNUC_INTERNAL -void sysprof_theme_manager_unregister (SysprofThemeManager *self, - guint registration_id); -G_GNUC_INTERNAL -guint sysprof_theme_manager_register_resource (SysprofThemeManager *self, - const gchar *theme_name, - const gchar *variant, - const gchar *resource); - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-time-label.c b/src/libsysprof-ui/sysprof-time-label.c deleted file mode 100644 index 7d129124..00000000 --- a/src/libsysprof-ui/sysprof-time-label.c +++ /dev/null @@ -1,117 +0,0 @@ -/* sysprof-time-label.c - * - * Copyright 2019 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 - */ - -#define G_LOG_DOMAIN "sysprof-time-label" - -#include "config.h" - -#include "sysprof-time-label.h" - -struct _SysprofTimeLabel -{ - GtkWidget parent_instance; - GtkCenterBox *box; - GtkLabel *minutes; - GtkLabel *seconds; -}; - -G_DEFINE_TYPE (SysprofTimeLabel, sysprof_time_label, GTK_TYPE_WIDGET) - -static void -sysprof_time_label_dispose (GObject *object) -{ - SysprofTimeLabel *self = (SysprofTimeLabel *)object; - - if (self->box) - { - gtk_widget_unparent (GTK_WIDGET (self->box)); - self->box = NULL; - } - - G_OBJECT_CLASS (sysprof_time_label_parent_class)->dispose (object); -} - -static void -sysprof_time_label_class_init (SysprofTimeLabelClass *klass) -{ - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->dispose = sysprof_time_label_dispose; - - gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT); -} - -static void -sysprof_time_label_init (SysprofTimeLabel *self) -{ - PangoAttrList *attrs = pango_attr_list_new (); - GtkWidget *sep; - - pango_attr_list_insert (attrs, pango_attr_scale_new (4)); - pango_attr_list_insert (attrs, pango_attr_weight_new (PANGO_WEIGHT_BOLD)); - - self->box = GTK_CENTER_BOX (gtk_center_box_new ()); - gtk_widget_set_parent (GTK_WIDGET (self->box), GTK_WIDGET (self)); - - self->minutes = g_object_new (GTK_TYPE_LABEL, - "attributes", attrs, - "xalign", 1.0f, - "hexpand", TRUE, - NULL); - gtk_center_box_set_start_widget (self->box, GTK_WIDGET (self->minutes)); - - sep = g_object_new (GTK_TYPE_LABEL, - "margin-start", 3, - "margin-end", 3, - "attributes", attrs, - "visible", TRUE, - "label", ":", - NULL); - gtk_center_box_set_center_widget (self->box, sep); - - self->seconds = g_object_new (GTK_TYPE_LABEL, - "attributes", attrs, - "visible", TRUE, - "xalign", 0.0f, - "hexpand", TRUE, - NULL); - gtk_center_box_set_end_widget (self->box, GTK_WIDGET (self->seconds)); -} - -void -sysprof_time_label_set_duration (SysprofTimeLabel *self, - guint duration) -{ - gchar minstr[12]; - gchar secstr[12]; - gint min, sec; - - g_return_if_fail (SYSPROF_IS_TIME_LABEL (self)); - - min = duration / 60; - sec = duration % 60; - - g_snprintf (minstr, sizeof minstr, "%02d", min); - g_snprintf (secstr, sizeof secstr, "%02d", sec); - - gtk_label_set_label (self->minutes, minstr); - gtk_label_set_label (self->seconds, secstr); -} diff --git a/src/libsysprof-ui/sysprof-time-label.h b/src/libsysprof-ui/sysprof-time-label.h deleted file mode 100644 index 7dfc5707..00000000 --- a/src/libsysprof-ui/sysprof-time-label.h +++ /dev/null @@ -1,34 +0,0 @@ -/* sysprof-time-label.h - * - * Copyright 2019 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_TIME_LABEL (sysprof_time_label_get_type()) - -G_DECLARE_FINAL_TYPE (SysprofTimeLabel, sysprof_time_label, SYSPROF, TIME_LABEL, GtkWidget) - -void sysprof_time_label_set_duration (SysprofTimeLabel *self, - guint duration); - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-time-visualizer.c b/src/libsysprof-ui/sysprof-time-visualizer.c deleted file mode 100644 index 58de9d35..00000000 --- a/src/libsysprof-ui/sysprof-time-visualizer.c +++ /dev/null @@ -1,543 +0,0 @@ -/* sysprof-time-visualizer.c - * - * Copyright 2016-2019 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 - */ - -#define G_LOG_DOMAIN "sysprof-time-visualizer" - -#include "config.h" - -#include -#include -#include -#include - -#include "pointcache.h" -#include "sysprof-time-visualizer.h" - -typedef struct -{ - /* - * Our reader as assigned by the visualizer system. - */ - SysprofCaptureReader *reader; - - /* - * An array of LineInfo which contains information about the counters - * we need to render. - */ - GArray *lines; - - /* - * This is our set of cached points to render. Once it is assigned here, - * it is immutable (and therefore may be shared with worker processes - * that are rendering the points). - */ - PointCache *cache; - - /* - * If we have a new counter discovered or the reader is set, we might - * want to delay loading until we return to the main loop. This can - * help us avoid doing duplicate work. - */ - guint queued_load; -} SysprofTimeVisualizerPrivate; - -typedef struct -{ - guint id; - gdouble line_width; - GdkRGBA rgba; - guint use_default_style : 1; - guint use_dash : 1; -} LineInfo; - -typedef struct -{ - SysprofCaptureCursor *cursor; - GArray *lines; - PointCache *cache; - gint64 begin_time; - gint64 end_time; -} LoadData; - -G_DEFINE_TYPE_WITH_PRIVATE (SysprofTimeVisualizer, sysprof_time_visualizer, SYSPROF_TYPE_VISUALIZER) - -static void sysprof_time_visualizer_load_data_async (SysprofTimeVisualizer *self, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -static PointCache *sysprof_time_visualizer_load_data_finish (SysprofTimeVisualizer *self, - GAsyncResult *result, - GError **error); - -static gdouble dashes[] = { 1.0, 2.0 }; - -static void -load_data_free (gpointer data) -{ - LoadData *load = data; - - if (load != NULL) - { - g_clear_pointer (&load->lines, g_array_unref); - g_clear_pointer (&load->cursor, sysprof_capture_cursor_unref); - g_clear_pointer (&load->cache, point_cache_unref); - g_slice_free (LoadData, load); - } -} - -static GArray * -copy_array (GArray *ar) -{ - GArray *ret; - - ret = g_array_sized_new (FALSE, FALSE, g_array_get_element_size (ar), ar->len); - g_array_set_size (ret, ar->len); - memcpy (ret->data, ar->data, ar->len * g_array_get_element_size (ret)); - - return ret; -} - -static void -sysprof_time_visualizer_snapshot (GtkWidget *widget, - GtkSnapshot *snapshot) -{ - SysprofTimeVisualizer *self = (SysprofTimeVisualizer *)widget; - SysprofTimeVisualizerPrivate *priv = sysprof_time_visualizer_get_instance_private (self); - GtkStyleContext *style_context; - cairo_t *cr; - GtkAllocation alloc; - GdkRGBA foreground; - - g_assert (SYSPROF_IS_TIME_VISUALIZER (widget)); - g_assert (snapshot != NULL); - - gtk_widget_get_allocation (widget, &alloc); - - GTK_WIDGET_CLASS (sysprof_time_visualizer_parent_class)->snapshot (widget, snapshot); - - if (priv->cache == NULL) - return; - -#if 0 - if (!gdk_cairo_get_clip_rectangle (cr, &clip)) - return ret; -#else - alloc.x = 0; - alloc.y = 0; -#endif - - style_context = gtk_widget_get_style_context (widget); - gtk_style_context_get_color (style_context, &foreground); - - cr = gtk_snapshot_append_cairo (snapshot, &GRAPHENE_RECT_INIT (0, 0, alloc.width, alloc.height)); - - gdk_cairo_set_source_rgba (cr, &foreground); - - for (guint line = 0; line < priv->lines->len; line++) - { - g_autofree SysprofVisualizerAbsolutePoint *points = NULL; - const LineInfo *line_info = &g_array_index (priv->lines, LineInfo, line); - const Point *fpoints; - guint n_fpoints = 0; - - fpoints = point_cache_get_points (priv->cache, line_info->id, &n_fpoints); - - if (n_fpoints > 0) - { - guint last_x = G_MAXUINT; - - points = g_new0 (SysprofVisualizerAbsolutePoint, n_fpoints); - - sysprof_visualizer_translate_points (SYSPROF_VISUALIZER (self), - (const SysprofVisualizerRelativePoint *)fpoints, - n_fpoints, - points, - n_fpoints); - - cairo_set_line_width (cr, 1.0); - - for (guint i = 0; i < n_fpoints; i++) - { - if ((guint)points[i].x != last_x) - last_x = (guint)points[i].x; - else - continue; - - cairo_move_to (cr, (guint)points[i].x + .5, alloc.height / 3); - cairo_line_to (cr, (guint)points[i].x + .5, alloc.height / 3 * 2); - } - - if (line_info->use_dash) - cairo_set_dash (cr, dashes, G_N_ELEMENTS (dashes), 0); - - cairo_stroke (cr); - } - } - - cairo_destroy (cr); -} - -static void -sysprof_time_visualizer_load_data_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - SysprofTimeVisualizer *self = (SysprofTimeVisualizer *)object; - SysprofTimeVisualizerPrivate *priv = sysprof_time_visualizer_get_instance_private (self); - g_autoptr(GError) error = NULL; - g_autoptr(PointCache) cache = NULL; - - g_assert (SYSPROF_IS_TIME_VISUALIZER (self)); - - cache = sysprof_time_visualizer_load_data_finish (self, result, &error); - - if (cache == NULL) - { - g_warning ("%s", error->message); - return; - } - - g_clear_pointer (&priv->cache, point_cache_unref); - priv->cache = g_steal_pointer (&cache); - - gtk_widget_queue_draw (GTK_WIDGET (self)); -} - -static gboolean -sysprof_time_visualizer_do_reload (gpointer data) -{ - SysprofTimeVisualizer *self = data; - SysprofTimeVisualizerPrivate *priv = sysprof_time_visualizer_get_instance_private (self); - - g_assert (SYSPROF_IS_TIME_VISUALIZER (self)); - - priv->queued_load = 0; - if (priv->reader != NULL) - sysprof_time_visualizer_load_data_async (self, - NULL, - sysprof_time_visualizer_load_data_cb, - NULL); - - return G_SOURCE_REMOVE; -} - -static void -sysprof_time_visualizer_queue_reload (SysprofTimeVisualizer *self) -{ - SysprofTimeVisualizerPrivate *priv = sysprof_time_visualizer_get_instance_private (self); - - g_assert (SYSPROF_IS_TIME_VISUALIZER (self)); - - if (priv->queued_load == 0) - priv->queued_load = - g_idle_add_full (G_PRIORITY_LOW, - sysprof_time_visualizer_do_reload, - self, - NULL); -} - -static void -sysprof_time_visualizer_set_reader (SysprofVisualizer *row, - SysprofCaptureReader *reader) -{ - SysprofTimeVisualizer *self = (SysprofTimeVisualizer *)row; - SysprofTimeVisualizerPrivate *priv = sysprof_time_visualizer_get_instance_private (self); - - g_assert (SYSPROF_IS_TIME_VISUALIZER (self)); - - if (priv->reader != reader) - { - if (priv->reader != NULL) - { - sysprof_capture_reader_unref (priv->reader); - priv->reader = NULL; - } - - if (reader != NULL) - priv->reader = sysprof_capture_reader_ref (reader); - - sysprof_time_visualizer_queue_reload (self); - } -} - -static void -sysprof_time_visualizer_finalize (GObject *object) -{ - SysprofTimeVisualizer *self = (SysprofTimeVisualizer *)object; - SysprofTimeVisualizerPrivate *priv = sysprof_time_visualizer_get_instance_private (self); - - g_clear_pointer (&priv->lines, g_array_unref); - g_clear_pointer (&priv->cache, point_cache_unref); - g_clear_pointer (&priv->reader, sysprof_capture_reader_unref); - - g_clear_handle_id (&priv->queued_load, g_source_remove); - - G_OBJECT_CLASS (sysprof_time_visualizer_parent_class)->finalize (object); -} - -static void -sysprof_time_visualizer_class_init (SysprofTimeVisualizerClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); - SysprofVisualizerClass *visualizer_class = SYSPROF_VISUALIZER_CLASS (klass); - - object_class->finalize = sysprof_time_visualizer_finalize; - - widget_class->snapshot = sysprof_time_visualizer_snapshot; - - visualizer_class->set_reader = sysprof_time_visualizer_set_reader; -} - -static void -sysprof_time_visualizer_init (SysprofTimeVisualizer *self) -{ - SysprofTimeVisualizerPrivate *priv = sysprof_time_visualizer_get_instance_private (self); - - priv->lines = g_array_new (FALSE, FALSE, sizeof (LineInfo)); -} - -void -sysprof_time_visualizer_add_counter (SysprofTimeVisualizer *self, - guint counter_id, - const GdkRGBA *color) -{ - SysprofTimeVisualizerPrivate *priv = sysprof_time_visualizer_get_instance_private (self); - LineInfo line_info = {0}; - - g_assert (SYSPROF_IS_TIME_VISUALIZER (self)); - g_assert (priv->lines != NULL); - - line_info.id = counter_id; - line_info.line_width = 1.0; - - if (color != NULL) - { - line_info.rgba = *color; - line_info.use_default_style = FALSE; - } - else - { - line_info.use_default_style = TRUE; - } - - g_array_append_val (priv->lines, line_info); - - if (SYSPROF_TIME_VISUALIZER_GET_CLASS (self)->counter_added) - SYSPROF_TIME_VISUALIZER_GET_CLASS (self)->counter_added (self, counter_id); - - sysprof_time_visualizer_queue_reload (self); -} - -void -sysprof_time_visualizer_clear (SysprofTimeVisualizer *self) -{ - SysprofTimeVisualizerPrivate *priv = sysprof_time_visualizer_get_instance_private (self); - - g_return_if_fail (SYSPROF_IS_TIME_VISUALIZER (self)); - - if (priv->lines->len > 0) - g_array_remove_range (priv->lines, 0, priv->lines->len); - - gtk_widget_queue_draw (GTK_WIDGET (self)); -} - -static inline gboolean -contains_id (GArray *ar, - guint id) -{ - for (guint i = 0; i < ar->len; i++) - { - const LineInfo *info = &g_array_index (ar, LineInfo, i); - - if (info->id == id) - return TRUE; - } - - return FALSE; -} - -static inline gdouble -calc_x (gint64 lower, - gint64 upper, - gint64 value) -{ - return (gdouble)(value - lower) / (gdouble)(upper - lower); -} - -static bool -sysprof_time_visualizer_load_data_frame_cb (const SysprofCaptureFrame *frame, - gpointer user_data) -{ - LoadData *load = user_data; - - g_assert (frame != NULL); - g_assert (frame->type == SYSPROF_CAPTURE_FRAME_CTRSET || - frame->type == SYSPROF_CAPTURE_FRAME_CTRDEF); - g_assert (load != NULL); - - if (frame->type == SYSPROF_CAPTURE_FRAME_CTRSET) - { - const SysprofCaptureCounterSet *set = (SysprofCaptureCounterSet *)frame; - gdouble x = calc_x (load->begin_time, load->end_time, frame->time); - - for (guint i = 0; i < set->n_values; i++) - { - const SysprofCaptureCounterValues *group = &set->values[i]; - - for (guint j = 0; j < G_N_ELEMENTS (group->ids); j++) - { - guint counter_id = group->ids[j]; - - if (counter_id != 0 && contains_id (load->lines, counter_id)) - point_cache_add_point_to_set (load->cache, counter_id, x, 0); - } - } - } - - return TRUE; -} - -static void -sysprof_time_visualizer_load_data_worker (GTask *task, - gpointer source_object, - gpointer task_data, - GCancellable *cancellable) -{ - LoadData *load = task_data; - g_autoptr(GArray) counter_ids = NULL; - - g_assert (G_IS_TASK (task)); - g_assert (SYSPROF_IS_TIME_VISUALIZER (source_object)); - g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); - - counter_ids = g_array_new (FALSE, FALSE, sizeof (guint)); - - for (guint i = 0; i < load->lines->len; i++) - { - const LineInfo *line_info = &g_array_index (load->lines, LineInfo, i); - g_array_append_val (counter_ids, line_info->id); - } - - sysprof_capture_cursor_add_condition (load->cursor, - sysprof_capture_condition_new_where_counter_in (counter_ids->len, - (guint *)(gpointer)counter_ids->data)); - sysprof_capture_cursor_foreach (load->cursor, sysprof_time_visualizer_load_data_frame_cb, load); - g_task_return_pointer (task, g_steal_pointer (&load->cache), (GDestroyNotify)point_cache_unref); -} - -static void -sysprof_time_visualizer_load_data_async (SysprofTimeVisualizer *self, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - SysprofTimeVisualizerPrivate *priv = sysprof_time_visualizer_get_instance_private (self); - g_autoptr(GTask) task = NULL; - LoadData *load; - - g_assert (SYSPROF_IS_TIME_VISUALIZER (self)); - g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); - - task = g_task_new (self, cancellable, callback, user_data); - g_task_set_priority (task, G_PRIORITY_LOW); - g_task_set_source_tag (task, sysprof_time_visualizer_load_data_async); - - if (priv->reader == NULL) - { - g_task_return_new_error (task, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "No data loaded"); - return; - } - - load = g_slice_new0 (LoadData); - load->cache = point_cache_new (); - load->begin_time = sysprof_capture_reader_get_start_time (priv->reader); - load->end_time = sysprof_capture_reader_get_end_time (priv->reader); - load->cursor = sysprof_capture_cursor_new (priv->reader); - load->lines = copy_array (priv->lines); - - for (guint i = 0; i < load->lines->len; i++) - { - const LineInfo *line_info = &g_array_index (load->lines, LineInfo, i); - - point_cache_add_set (load->cache, line_info->id); - } - - g_task_set_task_data (task, load, load_data_free); - g_task_run_in_thread (task, sysprof_time_visualizer_load_data_worker); -} - -static PointCache * -sysprof_time_visualizer_load_data_finish (SysprofTimeVisualizer *self, - GAsyncResult *result, - GError **error) -{ - g_assert (SYSPROF_IS_TIME_VISUALIZER (self)); - g_assert (G_IS_TASK (result)); - - return g_task_propagate_pointer (G_TASK (result), error); -} - -void -sysprof_time_visualizer_set_line_width (SysprofTimeVisualizer *self, - guint counter_id, - gdouble width) -{ - SysprofTimeVisualizerPrivate *priv = sysprof_time_visualizer_get_instance_private (self); - - g_return_if_fail (SYSPROF_IS_TIME_VISUALIZER (self)); - - for (guint i = 0; i < priv->lines->len; i++) - { - LineInfo *info = &g_array_index (priv->lines, LineInfo, i); - - if (info->id == counter_id) - { - info->line_width = width; - sysprof_time_visualizer_queue_reload (self); - break; - } - } -} - -void -sysprof_time_visualizer_set_dash (SysprofTimeVisualizer *self, - guint counter_id, - gboolean use_dash) -{ - SysprofTimeVisualizerPrivate *priv = sysprof_time_visualizer_get_instance_private (self); - - g_return_if_fail (SYSPROF_IS_TIME_VISUALIZER (self)); - - for (guint i = 0; i < priv->lines->len; i++) - { - LineInfo *info = &g_array_index (priv->lines, LineInfo, i); - - if (info->id == counter_id) - { - info->use_dash = !!use_dash; - sysprof_time_visualizer_queue_reload (self); - break; - } - } -} diff --git a/src/libsysprof-ui/sysprof-time-visualizer.h b/src/libsysprof-ui/sysprof-time-visualizer.h deleted file mode 100644 index 1b9160ac..00000000 --- a/src/libsysprof-ui/sysprof-time-visualizer.h +++ /dev/null @@ -1,54 +0,0 @@ -/* sysprof-time-visualizer.h - * - * Copyright 2016-2019 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-visualizer.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_TIME_VISUALIZER (sysprof_time_visualizer_get_type()) - -G_DECLARE_DERIVABLE_TYPE (SysprofTimeVisualizer, sysprof_time_visualizer, SYSPROF, TIME_VISUALIZER, SysprofVisualizer) - -struct _SysprofTimeVisualizerClass -{ - SysprofVisualizerClass parent_class; - - void (*counter_added) (SysprofTimeVisualizer *self, - guint counter_id); - - /*< private >*/ - gpointer _reserved[16]; -}; - -GtkWidget *sysprof_time_visualizer_new (void); -void sysprof_time_visualizer_clear (SysprofTimeVisualizer *self); -void sysprof_time_visualizer_add_counter (SysprofTimeVisualizer *self, - guint counter_id, - const GdkRGBA *color); -void sysprof_time_visualizer_set_line_width (SysprofTimeVisualizer *self, - guint counter_id, - gdouble width); -void sysprof_time_visualizer_set_dash (SysprofTimeVisualizer *self, - guint counter_id, - gboolean use_dash); - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-ui-private.h b/src/libsysprof-ui/sysprof-ui-private.h deleted file mode 100644 index 0216d3ba..00000000 --- a/src/libsysprof-ui/sysprof-ui-private.h +++ /dev/null @@ -1,48 +0,0 @@ -/* sysprof-ui-private.h - * - * Copyright 2019 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-callgraph-page.h" -#include "sysprof-display.h" -#include "sysprof-profiler-assistant.h" - -G_BEGIN_DECLS - -void _sysprof_callgraph_page_set_failed (SysprofCallgraphPage *self); -void _sysprof_callgraph_page_set_loading (SysprofCallgraphPage *self, - gboolean loading); -void _sysprof_memory_page_set_failed (SysprofCallgraphPage *self); -void _sysprof_memory_page_set_loading (SysprofCallgraphPage *self, - gboolean loading); -void _sysprof_display_focus_record (SysprofDisplay *self); -void _sysprof_display_reload_page (SysprofDisplay *self, - SysprofPage *page); -void _sysprof_profiler_assistant_focus_record (SysprofProfilerAssistant *self); -gchar *_sysprof_format_duration (gint64 duration); - -#if !GLIB_CHECK_VERSION(2, 56, 0) -# define g_clear_weak_pointer(ptr) \ - (*(ptr) ? (g_object_remove_weak_pointer((GObject*)*(ptr), (gpointer*)ptr),*(ptr)=NULL,1) : 0) -# define g_set_weak_pointer(ptr,obj) \ - ((obj!=*(ptr))?(g_clear_weak_pointer(ptr),*(ptr)=obj,((obj)?g_object_add_weak_pointer((GObject*)obj,(gpointer*)ptr),NULL:NULL),1):0) -#endif - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-ui.h b/src/libsysprof-ui/sysprof-ui.h deleted file mode 100644 index 02513d51..00000000 --- a/src/libsysprof-ui/sysprof-ui.h +++ /dev/null @@ -1,41 +0,0 @@ -/* sysprof-ui.h - * - * Copyright 2016-2019 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 - -G_BEGIN_DECLS - -#define SYSPROF_UI_INSIDE - -# include "sysprof-check.h" -# include "sysprof-display.h" -# include "sysprof-model-filter.h" -# include "sysprof-notebook.h" -# include "sysprof-page.h" -# include "sysprof-process-model-row.h" -# include "sysprof-visualizer-group.h" -# include "sysprof-visualizer.h" - -#undef SYSPROF_UI_INSIDE - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-visualizer-group-header.c b/src/libsysprof-ui/sysprof-visualizer-group-header.c deleted file mode 100644 index 8dbadca6..00000000 --- a/src/libsysprof-ui/sysprof-visualizer-group-header.c +++ /dev/null @@ -1,241 +0,0 @@ -/* sysprof-visualizer-group-header.c - * - * Copyright 2019 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 - */ - -#define G_LOG_DOMAIN "sysprof-visualizer-group-header" - -#include "config.h" - -#include - -#include "sysprof-visualizer.h" -#include "sysprof-visualizer-group.h" -#include "sysprof-visualizer-group-header.h" -#include "sysprof-visualizer-group-private.h" - -struct _SysprofVisualizerGroupHeader -{ - GtkListBoxRow parent_instance; - - SysprofVisualizerGroup *group; - GtkBox *box; -}; - -G_DEFINE_TYPE (SysprofVisualizerGroupHeader, sysprof_visualizer_group_header, GTK_TYPE_LIST_BOX_ROW) - -enum { - PROP_0, - PROP_GROUP, - N_PROPS -}; - -static GParamSpec *properties [N_PROPS]; - -static void -sysprof_visualizer_group_header_dispose (GObject *object) -{ - SysprofVisualizerGroupHeader *self = (SysprofVisualizerGroupHeader *)object; - - if (self->box) - { - gtk_widget_unparent (GTK_WIDGET (self->box)); - self->box = NULL; - } - - G_OBJECT_CLASS (sysprof_visualizer_group_header_parent_class)->dispose (object); -} - -static void -sysprof_visualizer_group_header_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - SysprofVisualizerGroupHeader *self = SYSPROF_VISUALIZER_GROUP_HEADER (object); - - switch (prop_id) - { - case PROP_GROUP: - g_value_set_object (value, _sysprof_visualizer_group_header_get_group (self)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_visualizer_group_header_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - SysprofVisualizerGroupHeader *self = SYSPROF_VISUALIZER_GROUP_HEADER (object); - - switch (prop_id) - { - case PROP_GROUP: - self->group = g_value_get_object (value); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_visualizer_group_header_class_init (SysprofVisualizerGroupHeaderClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); - - object_class->dispose = sysprof_visualizer_group_header_dispose; - object_class->get_property = sysprof_visualizer_group_header_get_property; - object_class->set_property = sysprof_visualizer_group_header_set_property; - - properties [PROP_GROUP] = - g_param_spec_object ("group", - "Group", - "The group", - SYSPROF_TYPE_VISUALIZER_GROUP, - (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_properties (object_class, N_PROPS, properties); - - gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT); -} - -static void -sysprof_visualizer_group_header_init (SysprofVisualizerGroupHeader *self) -{ - self->box = g_object_new (GTK_TYPE_BOX, - "orientation", GTK_ORIENTATION_VERTICAL, - "visible", TRUE, - NULL); - gtk_widget_set_parent (GTK_WIDGET (self->box), GTK_WIDGET (self)); -} - -void -_sysprof_visualizer_group_header_add_row (SysprofVisualizerGroupHeader *self, - guint position, - const gchar *title, - GMenuModel *menu, - GtkWidget *widget) -{ - GtkWidget *sibling; - GtkBox *box; - - g_return_if_fail (SYSPROF_IS_VISUALIZER_GROUP_HEADER (self)); - g_return_if_fail (SYSPROF_IS_VISUALIZER (widget)); - g_return_if_fail (!menu || G_IS_MENU_MODEL (menu)); - - box = g_object_new (GTK_TYPE_BOX, - "orientation", GTK_ORIENTATION_HORIZONTAL, - "spacing", 6, - "visible", TRUE, - NULL); - g_object_bind_property (widget, "visible", box, "visible", G_BINDING_SYNC_CREATE); - - sibling = gtk_widget_get_first_child (GTK_WIDGET (self->box)); - for (; position > 1 && sibling; position--) - sibling = gtk_widget_get_next_sibling (sibling); - gtk_box_insert_child_after (self->box, GTK_WIDGET (box), sibling); - - if (title != NULL) - { - g_autoptr(GtkSizeGroup) size_group = gtk_size_group_new (GTK_SIZE_GROUP_VERTICAL); - PangoAttrList *attrs = pango_attr_list_new (); - GtkLabel *label; - - pango_attr_list_insert (attrs, pango_attr_scale_new (0.83333)); - label = g_object_new (GTK_TYPE_LABEL, - "attributes", attrs, - "ellipsize", PANGO_ELLIPSIZE_MIDDLE, - "margin-top", 6, - "margin-bottom", 6, - "margin-start", 6, - "margin-end", 6, - "hexpand", TRUE, - "label", title, - "visible", TRUE, - "xalign", 0.0f, - NULL); - gtk_box_append (box, GTK_WIDGET (label)); - pango_attr_list_unref (attrs); - - gtk_size_group_add_widget (size_group, widget); - gtk_size_group_add_widget (size_group, GTK_WIDGET (box)); - } - - if (position == 0 && sysprof_visualizer_group_get_has_page (self->group)) - { - GtkImage *image; - - image = g_object_new (GTK_TYPE_IMAGE, - "icon-name", "view-paged-symbolic", - "tooltip-text", _("Select for more details"), - "pixel-size", 16, - "visible", TRUE, - NULL); - gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (image)), "dim-label"); - gtk_box_append (box, GTK_WIDGET (image)); - } - - if (menu != NULL) - { - GtkStyleContext *style_context; - GtkMenuButton *button; - - button = g_object_new (GTK_TYPE_MENU_BUTTON, - "child", g_object_new (GTK_TYPE_IMAGE, - "icon-name", "view-more-symbolic", - "visible", TRUE, - NULL), - "margin-end", 6, - "direction", GTK_ARROW_RIGHT, - "halign", GTK_ALIGN_CENTER, - "menu-model", menu, - "tooltip-text", _("Display supplemental graphs"), - "valign", GTK_ALIGN_CENTER, - "visible", TRUE, - NULL); - style_context = gtk_widget_get_style_context (GTK_WIDGET (button)); - gtk_style_context_add_class (style_context, "image-button"); - gtk_style_context_add_class (style_context, "small-button"); - gtk_style_context_add_class (style_context, "flat"); - - gtk_box_append (box, GTK_WIDGET (button)); - } -} - -SysprofVisualizerGroupHeader * -_sysprof_visualizer_group_header_new (SysprofVisualizerGroup *group) -{ - return g_object_new (SYSPROF_TYPE_VISUALIZER_GROUP_HEADER, - "group", group, - NULL); -} - -SysprofVisualizerGroup * -_sysprof_visualizer_group_header_get_group (SysprofVisualizerGroupHeader *self) -{ - g_return_val_if_fail (SYSPROF_IS_VISUALIZER_GROUP_HEADER(self), NULL); - - return self->group; -} diff --git a/src/libsysprof-ui/sysprof-visualizer-group-header.h b/src/libsysprof-ui/sysprof-visualizer-group-header.h deleted file mode 100644 index 2a1e9c82..00000000 --- a/src/libsysprof-ui/sysprof-visualizer-group-header.h +++ /dev/null @@ -1,31 +0,0 @@ -/* sysprof-visualizer-group-header.h - * - * Copyright 2019 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_VISUALIZER_GROUP_HEADER (sysprof_visualizer_group_header_get_type()) - -G_DECLARE_FINAL_TYPE (SysprofVisualizerGroupHeader, sysprof_visualizer_group_header, SYSPROF, VISUALIZER_GROUP_HEADER, GtkListBoxRow) - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-visualizer-group-private.h b/src/libsysprof-ui/sysprof-visualizer-group-private.h deleted file mode 100644 index a23b6127..00000000 --- a/src/libsysprof-ui/sysprof-visualizer-group-private.h +++ /dev/null @@ -1,45 +0,0 @@ -/* sysprof-visualizers-group-private.h - * - * Copyright 2019 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-visualizer-group.h" -#include "sysprof-visualizer-group-header.h" - -G_BEGIN_DECLS - -void _sysprof_visualizer_group_set_reader (SysprofVisualizerGroup *self, - SysprofCaptureReader *reader); -SysprofVisualizerGroupHeader *_sysprof_visualizer_group_header_new (SysprofVisualizerGroup *group); -void _sysprof_visualizer_group_header_add_row (SysprofVisualizerGroupHeader *self, - guint position, - const gchar *title, - GMenuModel *menu, - GtkWidget *row); -void _sysprof_visualizer_group_header_remove_row (SysprofVisualizerGroupHeader *self, - guint row); -SysprofVisualizerGroup *_sysprof_visualizer_group_header_get_group (SysprofVisualizerGroupHeader *self); -void _sysprof_visualizer_group_set_header (SysprofVisualizerGroup *self, - SysprofVisualizerGroupHeader *header); - - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-visualizer-group.c b/src/libsysprof-ui/sysprof-visualizer-group.c deleted file mode 100644 index 2053e036..00000000 --- a/src/libsysprof-ui/sysprof-visualizer-group.c +++ /dev/null @@ -1,471 +0,0 @@ -/* sysprof-visualizer-group.c - * - * Copyright 2019 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 - */ - -#define G_LOG_DOMAIN "sysprof-visualizer-group" - -#include "config.h" - -#include - -#include "sysprof-visualizer.h" -#include "sysprof-visualizer-group.h" -#include "sysprof-visualizer-group-private.h" - -typedef struct -{ - /* Owned pointers */ - GMenuModel *menu; - GMenu *default_menu; - GMenu *rows_menu; - gchar *title; - GtkSizeGroup *size_group; - GSimpleActionGroup *actions; - - gint priority; - - guint has_page : 1; - - /* Weak pointers */ - SysprofVisualizerGroupHeader *header; - - /* Child Widgets */ - GtkBox *visualizers; -} SysprofVisualizerGroupPrivate; - -G_DEFINE_TYPE_WITH_PRIVATE (SysprofVisualizerGroup, sysprof_visualizer_group, GTK_TYPE_LIST_BOX_ROW) - -enum { - PROP_0, - PROP_HAS_PAGE, - PROP_MENU, - PROP_PRIORITY, - PROP_TITLE, - N_PROPS -}; - -enum { - GROUP_ACTIVATED, - N_SIGNALS -}; - -static GParamSpec *properties [N_PROPS]; -static guint signals [N_SIGNALS]; - -/** - * sysprof_visualizer_group_new: - * - * Create a new #SysprofVisualizerGroup. - * - * Returns: (transfer full): a newly created #SysprofVisualizerGroup - */ -SysprofVisualizerGroup * -sysprof_visualizer_group_new (void) -{ - return g_object_new (SYSPROF_TYPE_VISUALIZER_GROUP, NULL); -} - -const gchar * -sysprof_visualizer_group_get_title (SysprofVisualizerGroup *self) -{ - SysprofVisualizerGroupPrivate *priv = sysprof_visualizer_group_get_instance_private (self); - - g_return_val_if_fail (SYSPROF_IS_VISUALIZER_GROUP (self), NULL); - - return priv->title; -} - -void -sysprof_visualizer_group_set_title (SysprofVisualizerGroup *self, - const gchar *title) -{ - SysprofVisualizerGroupPrivate *priv = sysprof_visualizer_group_get_instance_private (self); - - g_return_if_fail (SYSPROF_IS_VISUALIZER_GROUP (self)); - - if (g_strcmp0 (priv->title, title) != 0) - { - g_free (priv->title); - priv->title = g_strdup (title); - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_TITLE]); - } -} - -/** - * sysprof_visualizer_group_get_menu: - * - * Gets the menu for the group. - * - * Returns: (transfer none) (nullable): a #GMenuModel or %NULL - * - * Since: 3.34 - */ -GMenuModel * -sysprof_visualizer_group_get_menu (SysprofVisualizerGroup *self) -{ - SysprofVisualizerGroupPrivate *priv = sysprof_visualizer_group_get_instance_private (self); - - g_return_val_if_fail (SYSPROF_IS_VISUALIZER_GROUP (self), NULL); - - return priv->menu; -} - -void -sysprof_visualizer_group_set_menu (SysprofVisualizerGroup *self, - GMenuModel *menu) -{ - SysprofVisualizerGroupPrivate *priv = sysprof_visualizer_group_get_instance_private (self); - - g_return_if_fail (SYSPROF_IS_VISUALIZER_GROUP (self)); - g_return_if_fail (!menu || G_IS_MENU_MODEL (menu)); - - if (g_set_object (&priv->menu, menu)) - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_MENU]); -} - -static gchar * -create_action_name (const gchar *str) -{ - GString *ret = g_string_new (NULL); - - for (; *str; str = g_utf8_next_char (str)) - { - gunichar ch = g_utf8_get_char (str); - - if (g_unichar_isalnum (ch)) - g_string_append_unichar (ret, ch); - else - g_string_append_c (ret, '_'); - } - - return g_string_free (ret, FALSE); -} - -static void -sysprof_visualizer_group_finalize (GObject *object) -{ - SysprofVisualizerGroup *self = (SysprofVisualizerGroup *)object; - SysprofVisualizerGroupPrivate *priv = sysprof_visualizer_group_get_instance_private (self); - - g_clear_pointer (&priv->title, g_free); - g_clear_object (&priv->menu); - g_clear_object (&priv->size_group); - g_clear_object (&priv->default_menu); - g_clear_object (&priv->rows_menu); - g_clear_object (&priv->actions); - - g_clear_weak_pointer (&priv->header); - - G_OBJECT_CLASS (sysprof_visualizer_group_parent_class)->finalize (object); -} - -static void -sysprof_visualizer_group_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - SysprofVisualizerGroup *self = SYSPROF_VISUALIZER_GROUP (object); - - switch (prop_id) - { - case PROP_HAS_PAGE: - g_value_set_boolean (value, sysprof_visualizer_group_get_has_page (self)); - break; - - case PROP_MENU: - g_value_set_object (value, sysprof_visualizer_group_get_menu (self)); - break; - - case PROP_PRIORITY: - g_value_set_int (value, sysprof_visualizer_group_get_priority (self)); - break; - - case PROP_TITLE: - g_value_set_string (value, sysprof_visualizer_group_get_title (self)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_visualizer_group_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - SysprofVisualizerGroup *self = SYSPROF_VISUALIZER_GROUP (object); - - switch (prop_id) - { - case PROP_HAS_PAGE: - sysprof_visualizer_group_set_has_page (self, g_value_get_boolean (value)); - break; - - case PROP_MENU: - sysprof_visualizer_group_set_menu (self, g_value_get_object (value)); - break; - - case PROP_PRIORITY: - sysprof_visualizer_group_set_priority (self, g_value_get_int (value)); - break; - - case PROP_TITLE: - sysprof_visualizer_group_set_title (self, g_value_get_string (value)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_visualizer_group_class_init (SysprofVisualizerGroupClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); - - object_class->finalize = sysprof_visualizer_group_finalize; - object_class->get_property = sysprof_visualizer_group_get_property; - object_class->set_property = sysprof_visualizer_group_set_property; - - properties [PROP_HAS_PAGE] = - g_param_spec_boolean ("has-page", - "Has Page", - "Has Page", - FALSE, - (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); - - properties [PROP_MENU] = - g_param_spec_object ("menu", - "Menu", - "Menu", - G_TYPE_MENU_MODEL, - (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); - - properties [PROP_PRIORITY] = - g_param_spec_int ("priority", - "Priority", - "The Priority of the group, used for sorting", - G_MININT, G_MAXINT, 0, - (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); - - properties [PROP_TITLE] = - g_param_spec_string ("title", - "Title", - "The title of the row", - NULL, - (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_properties (object_class, N_PROPS, properties); - - signals [GROUP_ACTIVATED] = - g_signal_new ("group-activated", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, - NULL, - G_TYPE_NONE, 0); - - gtk_widget_class_set_css_name (widget_class, "SysprofVisualizerGroup"); -} - -static void -sysprof_visualizer_group_init (SysprofVisualizerGroup *self) -{ - SysprofVisualizerGroupPrivate *priv = sysprof_visualizer_group_get_instance_private (self); - g_autoptr(GMenuItem) item = NULL; - - priv->actions = g_simple_action_group_new (); - - priv->default_menu = g_menu_new (); - priv->rows_menu = g_menu_new (); - - item = g_menu_item_new_section (NULL, G_MENU_MODEL (priv->rows_menu)); - g_menu_append_item (priv->default_menu, item); - - priv->menu = g_object_ref (G_MENU_MODEL (priv->default_menu)); - - priv->size_group = gtk_size_group_new (GTK_SIZE_GROUP_VERTICAL); - gtk_size_group_add_widget (priv->size_group, GTK_WIDGET (self)); - - priv->visualizers = g_object_new (GTK_TYPE_BOX, - "orientation", GTK_ORIENTATION_VERTICAL, - "visible", TRUE, - NULL); - gtk_list_box_row_set_child (GTK_LIST_BOX_ROW (self), GTK_WIDGET (priv->visualizers)); -} - -void -_sysprof_visualizer_group_set_header (SysprofVisualizerGroup *self, - SysprofVisualizerGroupHeader *header) -{ - SysprofVisualizerGroupPrivate *priv = sysprof_visualizer_group_get_instance_private (self); - - g_return_if_fail (SYSPROF_IS_VISUALIZER_GROUP (self)); - g_return_if_fail (!header || SYSPROF_IS_VISUALIZER_GROUP_HEADER (header)); - - if (g_set_weak_pointer (&priv->header, header)) - { - if (header != NULL) - { - guint position = 0; - - gtk_widget_insert_action_group (GTK_WIDGET (header), - "group", - G_ACTION_GROUP (priv->actions)); - gtk_size_group_add_widget (priv->size_group, GTK_WIDGET (header)); - - for (GtkWidget *child = gtk_widget_get_first_child (GTK_WIDGET (priv->visualizers)); - child; - child = gtk_widget_get_next_sibling (child)) - { - SysprofVisualizer *vis = SYSPROF_VISUALIZER (child); - const gchar *title; - GMenuModel *menu = NULL; - - g_assert (SYSPROF_IS_VISUALIZER (vis)); - - if (position == 0) - menu = priv->menu; - - title = sysprof_visualizer_get_title (vis); - - if (title == NULL) - title = priv->title; - - _sysprof_visualizer_group_header_add_row (header, - position, - title, - menu, - GTK_WIDGET (vis)); - - position++; - } - } - } -} - -void -_sysprof_visualizer_group_set_reader (SysprofVisualizerGroup *self, - SysprofCaptureReader *reader) -{ - SysprofVisualizerGroupPrivate *priv = sysprof_visualizer_group_get_instance_private (self); - - g_return_if_fail (SYSPROF_IS_VISUALIZER_GROUP (self)); - g_return_if_fail (reader != NULL); - - for (GtkWidget *child = gtk_widget_get_first_child (GTK_WIDGET (priv->visualizers)); - child; - child = gtk_widget_get_next_sibling (child)) - sysprof_visualizer_set_reader (SYSPROF_VISUALIZER (child), reader); -} - -void -sysprof_visualizer_group_insert (SysprofVisualizerGroup *self, - SysprofVisualizer *visualizer, - gint position, - gboolean can_toggle) -{ - SysprofVisualizerGroupPrivate *priv = sysprof_visualizer_group_get_instance_private (self); - GtkWidget *sibling = NULL; - - g_return_if_fail (SYSPROF_IS_VISUALIZER_GROUP (self)); - g_return_if_fail (SYSPROF_IS_VISUALIZER (visualizer)); - - if (position > 0) - { - sibling = gtk_widget_get_first_child (GTK_WIDGET (priv->visualizers)); - while (position > 1 && sibling) - { - sibling = gtk_widget_get_next_sibling (sibling); - position--; - } - } - gtk_box_insert_child_after (priv->visualizers, GTK_WIDGET (visualizer), sibling); - - if (can_toggle) - { - const gchar *title = sysprof_visualizer_get_title (visualizer); - g_autofree gchar *action_name = create_action_name (title); - g_autofree gchar *full_action_name = g_strdup_printf ("group.%s", action_name); - g_autoptr(GMenuItem) item = g_menu_item_new (title, full_action_name); - g_autoptr(GPropertyAction) action = NULL; - - action = g_property_action_new (action_name, visualizer, "visible"); - g_action_map_add_action (G_ACTION_MAP (priv->actions), G_ACTION (action)); - g_menu_item_set_attribute (item, "role", "s", "check"); - g_menu_append_item (priv->rows_menu, item); - } -} - -gint -sysprof_visualizer_group_get_priority (SysprofVisualizerGroup *self) -{ - SysprofVisualizerGroupPrivate *priv = sysprof_visualizer_group_get_instance_private (self); - - g_return_val_if_fail (SYSPROF_IS_VISUALIZER_GROUP (self), 0); - - return priv->priority; -} - -void -sysprof_visualizer_group_set_priority (SysprofVisualizerGroup *self, - gint priority) -{ - SysprofVisualizerGroupPrivate *priv = sysprof_visualizer_group_get_instance_private (self); - - g_return_if_fail (SYSPROF_IS_VISUALIZER_GROUP (self)); - - if (priv->priority != priority) - { - priv->priority = priority; - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_PRIORITY]); - } -} - -gboolean -sysprof_visualizer_group_get_has_page (SysprofVisualizerGroup *self) -{ - SysprofVisualizerGroupPrivate *priv = sysprof_visualizer_group_get_instance_private (self); - - g_return_val_if_fail (SYSPROF_IS_VISUALIZER_GROUP (self), FALSE); - - return priv->has_page; -} - -void -sysprof_visualizer_group_set_has_page (SysprofVisualizerGroup *self, - gboolean has_page) -{ - SysprofVisualizerGroupPrivate *priv = sysprof_visualizer_group_get_instance_private (self); - - g_return_if_fail (SYSPROF_IS_VISUALIZER_GROUP (self)); - - has_page = !!has_page; - - if (has_page != priv->has_page) - { - priv->has_page = has_page; - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_HAS_PAGE]); - } -} diff --git a/src/libsysprof-ui/sysprof-visualizer-group.h b/src/libsysprof-ui/sysprof-visualizer-group.h deleted file mode 100644 index cac1c661..00000000 --- a/src/libsysprof-ui/sysprof-visualizer-group.h +++ /dev/null @@ -1,76 +0,0 @@ -/* sysprof-visualizer-group.h - * - * Copyright 2019 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 - -#if !defined (SYSPROF_UI_INSIDE) && !defined (SYSPROF_UI_COMPILATION) -# error "Only can be included directly." -#endif - -#include - -#include "sysprof-visualizer.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_VISUALIZER_GROUP (sysprof_visualizer_group_get_type()) - -SYSPROF_AVAILABLE_IN_ALL -G_DECLARE_DERIVABLE_TYPE (SysprofVisualizerGroup, sysprof_visualizer_group, SYSPROF, VISUALIZER_GROUP, GtkListBoxRow) - -struct _SysprofVisualizerGroupClass -{ - GtkListBoxRowClass parent_class; - - void (*group_activated) (SysprofVisualizerGroup *self); - - /*< private >*/ - gpointer _reserved[16]; -}; - -SYSPROF_AVAILABLE_IN_ALL -SysprofVisualizerGroup *sysprof_visualizer_group_new (void); -SYSPROF_AVAILABLE_IN_ALL -GMenuModel *sysprof_visualizer_group_get_menu (SysprofVisualizerGroup *self); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_visualizer_group_set_menu (SysprofVisualizerGroup *self, - GMenuModel *menu); -SYSPROF_AVAILABLE_IN_ALL -gint sysprof_visualizer_group_get_priority (SysprofVisualizerGroup *self); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_visualizer_group_set_priority (SysprofVisualizerGroup *self, - gint priority); -SYSPROF_AVAILABLE_IN_ALL -const gchar *sysprof_visualizer_group_get_title (SysprofVisualizerGroup *self); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_visualizer_group_set_title (SysprofVisualizerGroup *self, - const gchar *title); -SYSPROF_AVAILABLE_IN_ALL -gboolean sysprof_visualizer_group_get_has_page (SysprofVisualizerGroup *self); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_visualizer_group_set_has_page (SysprofVisualizerGroup *self, - gboolean has_page); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_visualizer_group_insert (SysprofVisualizerGroup *self, - SysprofVisualizer *visualizer, - gint position, - gboolean can_toggle); - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-visualizer-ticks.c b/src/libsysprof-ui/sysprof-visualizer-ticks.c deleted file mode 100644 index 1f7049c8..00000000 --- a/src/libsysprof-ui/sysprof-visualizer-ticks.c +++ /dev/null @@ -1,324 +0,0 @@ -/* sysprof-visualizer-ticks.c - * - * Copyright 2016-2019 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 - */ - -#define G_LOG_DOMAIN "sysprof-visualizer-ticks" - -#include "config.h" - -#include -#include - -#include "sysprof-visualizer-ticks.h" - -#define NSEC_PER_DAY (SYSPROF_NSEC_PER_SEC * 60L * 60L * 24L) -#define NSEC_PER_HOUR (SYSPROF_NSEC_PER_SEC * 60L * 60L) -#define NSEC_PER_MIN (SYSPROF_NSEC_PER_SEC * 60L) -#define NSEC_PER_MSEC (SYSPROF_NSEC_PER_SEC/G_GINT64_CONSTANT(1000)) -#define MIN_TICK_DISTANCE 20 -#define LABEL_HEIGHT_PX 10 - -struct _SysprofVisualizerTicks -{ - SysprofVisualizer parent_instance; -}; - -enum { - TICK_MINUTES, - TICK_HALF_MINUTES, - TICK_FIVE_SECONDS, - TICK_SECONDS, - TICK_HALF_SECONDS, - TICK_QUARTER_SECONDS, - TICK_TENTHS, - TICK_HUNDREDTHS, - TICK_THOUSANDTHS, - TICK_TEN_THOUSANDTHS, - N_TICKS -}; - -struct { - gint width; - gint height; - gint64 span; -} tick_sizing[N_TICKS] = { - { 1, 12, SYSPROF_NSEC_PER_SEC * 60 }, - { 1, 11, SYSPROF_NSEC_PER_SEC * 30 }, - { 1, 10, SYSPROF_NSEC_PER_SEC * 5 }, - { 1, 9, SYSPROF_NSEC_PER_SEC }, - { 1, 8, SYSPROF_NSEC_PER_SEC / 2 }, - { 1, 6, SYSPROF_NSEC_PER_SEC / 4 }, - { 1, 5, SYSPROF_NSEC_PER_SEC / 10 }, - { 1, 4, SYSPROF_NSEC_PER_SEC / 100 }, - { 1, 3, SYSPROF_NSEC_PER_SEC / 1000 }, - { 1, 1, SYSPROF_NSEC_PER_SEC / 10000 }, -}; - -G_DEFINE_TYPE (SysprofVisualizerTicks, sysprof_visualizer_ticks, SYSPROF_TYPE_VISUALIZER) - -static void -update_label_text (PangoLayout *layout, - gint64 time, - gboolean want_msec) -{ - g_autofree gchar *str = NULL; - gint64 tmp; - gint msec = 0; - gint hours = 0; - gint min = 0; - gint sec = 0; - G_GNUC_UNUSED gint days = 0; - - g_assert (PANGO_IS_LAYOUT (layout)); - - tmp = time % SYSPROF_NSEC_PER_SEC; - time -= tmp; - msec = tmp / 100000L; - - if (time >= NSEC_PER_DAY) - { - days = time / NSEC_PER_DAY; - time %= NSEC_PER_DAY; - } - - if (time >= NSEC_PER_HOUR) - { - hours = time / NSEC_PER_HOUR; - time %= NSEC_PER_HOUR; - } - - if (time >= NSEC_PER_MIN) - { - min = time / NSEC_PER_MIN; - time %= NSEC_PER_MIN; - } - - if (time >= SYSPROF_NSEC_PER_SEC) - { - sec = time / SYSPROF_NSEC_PER_SEC; - time %= SYSPROF_NSEC_PER_SEC; - } - - if (want_msec || (!hours && !min && !sec && msec)) - { - if (hours > 0) - str = g_strdup_printf ("%02u:%02u:%02u.%04u", hours, min, sec, msec); - else - str = g_strdup_printf ("%02u:%02u.%04u", min, sec, msec); - } - else - { - if (hours > 0) - str = g_strdup_printf ("%02u:%02u:%02u", hours, min, sec); - else - str = g_strdup_printf ("%02u:%02u", min, sec); - } - - pango_layout_set_text (layout, str, -1); -} - -static gboolean -draw_ticks (SysprofVisualizerTicks *self, - GtkSnapshot *snapshot, - GtkAllocation *area, - gint ticks, - gboolean label_mode, - const GdkRGBA *color) -{ - GtkAllocation alloc; - gint64 begin_time, end_time; - gdouble half; - gint count = 0; - - g_assert (SYSPROF_IS_VISUALIZER_TICKS (self)); - g_assert (snapshot != NULL); - g_assert (area != NULL); - g_assert (ticks >= 0); - g_assert (ticks < N_TICKS); - - begin_time = sysprof_visualizer_get_begin_time (SYSPROF_VISUALIZER (self)); - end_time = sysprof_visualizer_get_end_time (SYSPROF_VISUALIZER (self)); - - /* - * If we are in label_model, we don't draw the ticks but only the labels. - * That way we can determine which tick level managed to draw a tick in - * the visible region so we only show labels for the largest tick level. - * (Returning true from this method indicates we successfully drew a tick). - */ - - half = tick_sizing[ticks].width / 2.0; - - gtk_widget_get_allocation (GTK_WIDGET (self), &alloc); - - if G_UNLIKELY (label_mode) - { - PangoFontDescription *font_desc; - PangoLayout *layout; - gboolean want_msec; - gint last_x2 = G_MININT; - gint w, h; - - layout = gtk_widget_create_pango_layout (GTK_WIDGET (self), "00:10:00.0000"); - - font_desc = pango_font_description_new (); - pango_font_description_set_family_static (font_desc, "Monospace"); - pango_font_description_set_absolute_size (font_desc, LABEL_HEIGHT_PX * PANGO_SCALE); - pango_layout_set_font_description (layout, font_desc); - pango_font_description_free (font_desc); - - pango_layout_get_pixel_size (layout, &w, &h); - - /* If we are operating on smaller than seconds here, then we want - * to ensure we include msec with the timestamps. - */ - want_msec = tick_sizing[ticks].span < SYSPROF_NSEC_PER_SEC; - - for (gint64 t = begin_time; t <= end_time; t += tick_sizing[ticks].span) - { - gdouble x = sysprof_visualizer_get_x_for_time (SYSPROF_VISUALIZER (self), t); - - if (x < (last_x2 + MIN_TICK_DISTANCE)) - continue; - - update_label_text (layout, t - begin_time, want_msec); - pango_layout_get_pixel_size (layout, &w, &h); - - if (x + w <= alloc.width) - { - gtk_snapshot_save (snapshot); - gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT ((int)x + 2.5 - (int)half, 2)); - gtk_snapshot_append_layout (snapshot, layout, color); - gtk_snapshot_restore (snapshot); - } - - last_x2 = x + w; - } - - g_clear_object (&layout); - } - else - { - for (gint64 t = begin_time; t <= end_time; t += tick_sizing[ticks].span) - { - gdouble x = sysprof_visualizer_get_x_for_time (SYSPROF_VISUALIZER (self), t); - - gtk_snapshot_append_color (snapshot, color, - &GRAPHENE_RECT_INIT ((int)x - .5 - (int)half, - alloc.height, - (int)x - .5 - (int)half + tick_sizing[ticks].width, - alloc.height - tick_sizing[ticks].height)); - count++; - } - } - - return count > 2; -} - -static void -sysprof_visualizer_ticks_snapshot (GtkWidget *widget, - GtkSnapshot *snapshot) -{ - SysprofVisualizerTicks *self = SYSPROF_VISUALIZER_TICKS (widget); - GtkStyleContext *style; - GtkAllocation alloc; - gint64 timespan; - GdkRGBA color; - - g_assert (SYSPROF_IS_VISUALIZER_TICKS (self)); - g_assert (snapshot != NULL); - - timespan = sysprof_visualizer_get_duration (SYSPROF_VISUALIZER (self)); - if (timespan == 0) - return; - - gtk_widget_get_allocation (GTK_WIDGET (self), &alloc); - alloc.x = 0; - alloc.y = 0; - - style = gtk_widget_get_style_context (widget); - gtk_style_context_get_color (style, &color); - - gtk_snapshot_render_background (snapshot, style, 0, 0, alloc.width, alloc.height); - - /* - * We need to discover up to what level we will draw tick marks. - * This is based on the width of the widget and the number of ticks - * to draw (which is determined from our timespan). We will skip a - * mark if they will end up less than MIN_TICK_DISTANCE px apart. - */ - - for (guint i = G_N_ELEMENTS (tick_sizing); i > 0; i--) - { - gint64 n_ticks = timespan / tick_sizing[i - 1].span; - gint largest_match = -1; - - if (n_ticks == 0 || (alloc.width / n_ticks) < MIN_TICK_DISTANCE) - continue; - - for (guint j = i; j > 0; j--) - { - if (draw_ticks (self, snapshot, &alloc, j - 1, FALSE, &color)) - largest_match = j - 1; - } - - if (largest_match != -1) - draw_ticks (self, snapshot, &alloc, largest_match, TRUE, &color); - - break; - } -} - -static void -sysprof_visualizer_ticks_measure (GtkWidget *widget, - GtkOrientation orientation, - int for_size, - int *minimum, - int *natural, - int *minimum_baseline, - int *natural_baseline) -{ - g_assert (SYSPROF_IS_VISUALIZER_TICKS (widget)); - - if (orientation == GTK_ORIENTATION_VERTICAL) - *minimum = *natural = tick_sizing[0].height + LABEL_HEIGHT_PX; - else - *minimum = *natural = 0; -} - -static void -sysprof_visualizer_ticks_class_init (SysprofVisualizerTicksClass *klass) -{ - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); - - widget_class->snapshot = sysprof_visualizer_ticks_snapshot; - widget_class->measure = sysprof_visualizer_ticks_measure; - - gtk_widget_class_set_css_name (widget_class, "SysprofVisualizerTicks"); -} - -static void -sysprof_visualizer_ticks_init (SysprofVisualizerTicks *self) -{ -} - -GtkWidget * -sysprof_visualizer_ticks_new (void) -{ - return g_object_new (SYSPROF_TYPE_VISUALIZER_TICKS, NULL); -} diff --git a/src/libsysprof-ui/sysprof-visualizer-ticks.h b/src/libsysprof-ui/sysprof-visualizer-ticks.h deleted file mode 100644 index cb4d6f5e..00000000 --- a/src/libsysprof-ui/sysprof-visualizer-ticks.h +++ /dev/null @@ -1,35 +0,0 @@ -/* sysprof-visualizer-ticks.h - * - * Copyright 2016-2019 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-visualizer.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_VISUALIZER_TICKS (sysprof_visualizer_ticks_get_type()) - -G_DECLARE_FINAL_TYPE (SysprofVisualizerTicks, sysprof_visualizer_ticks, SYSPROF, VISUALIZER_TICKS, SysprofVisualizer) - -GtkWidget *sysprof_visualizer_ticks_new (void); - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-visualizer.c b/src/libsysprof-ui/sysprof-visualizer.c deleted file mode 100644 index 23cb75f0..00000000 --- a/src/libsysprof-ui/sysprof-visualizer.c +++ /dev/null @@ -1,289 +0,0 @@ -/* sysprof-visualizer.c - * - * Copyright 2019 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 - */ - -#define G_LOG_DOMAIN "sysprof-visualizer" - -#include "config.h" - -#include "sysprof-visualizer.h" - -typedef struct -{ - gchar *title; - - gint64 begin_time; - gint64 end_time; - gint64 duration; -} SysprofVisualizerPrivate; - -G_DEFINE_TYPE_WITH_PRIVATE (SysprofVisualizer, sysprof_visualizer, GTK_TYPE_WIDGET) - -enum { - PROP_0, - PROP_BEGIN_TIME, - PROP_END_TIME, - PROP_TITLE, - N_PROPS -}; - -static GParamSpec *properties [N_PROPS]; - -static void -sysprof_visualizer_finalize (GObject *object) -{ - SysprofVisualizer *self = (SysprofVisualizer *)object; - SysprofVisualizerPrivate *priv = sysprof_visualizer_get_instance_private (self); - - g_clear_pointer (&priv->title, g_free); - - G_OBJECT_CLASS (sysprof_visualizer_parent_class)->finalize (object); -} - -static void -sysprof_visualizer_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - SysprofVisualizer *self = SYSPROF_VISUALIZER (object); - - switch (prop_id) - { - case PROP_TITLE: - g_value_set_string (value, sysprof_visualizer_get_title (self)); - break; - - case PROP_BEGIN_TIME: - g_value_set_int64 (value, sysprof_visualizer_get_begin_time (self)); - break; - - case PROP_END_TIME: - g_value_set_int64 (value, sysprof_visualizer_get_end_time (self)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_visualizer_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - SysprofVisualizer *self = SYSPROF_VISUALIZER (object); - SysprofVisualizerPrivate *priv = sysprof_visualizer_get_instance_private (self); - - switch (prop_id) - { - case PROP_TITLE: - sysprof_visualizer_set_title (self, g_value_get_string (value)); - break; - - case PROP_BEGIN_TIME: - priv->begin_time = g_value_get_int64 (value); - priv->duration = priv->end_time - priv->begin_time; - break; - - case PROP_END_TIME: - priv->end_time = g_value_get_int64 (value); - priv->duration = priv->end_time - priv->begin_time; - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_visualizer_class_init (SysprofVisualizerClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); - - object_class->finalize = sysprof_visualizer_finalize; - object_class->get_property = sysprof_visualizer_get_property; - object_class->set_property = sysprof_visualizer_set_property; - - properties [PROP_BEGIN_TIME] = - g_param_spec_int64 ("begin-time", - "Begin Time", - "Begin Time", - G_MININT64, - G_MAXINT64, - 0, - (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); - - properties [PROP_END_TIME] = - g_param_spec_int64 ("end-time", - "End Time", - "End Time", - G_MININT64, - G_MAXINT64, - 0, - (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); - - properties [PROP_TITLE] = - g_param_spec_string ("title", - "Title", - "The title for the row", - NULL, - (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_properties (object_class, N_PROPS, properties); - - gtk_widget_class_set_css_name (widget_class, "SysprofVisualizer"); -} - -static void -sysprof_visualizer_init (SysprofVisualizer *self) -{ -} - -const gchar * -sysprof_visualizer_get_title (SysprofVisualizer *self) -{ - SysprofVisualizerPrivate *priv = sysprof_visualizer_get_instance_private (self); - - g_return_val_if_fail (SYSPROF_IS_VISUALIZER (self), 0); - - return priv->title; -} - -void -sysprof_visualizer_set_title (SysprofVisualizer *self, - const gchar *title) -{ - SysprofVisualizerPrivate *priv = sysprof_visualizer_get_instance_private (self); - - g_return_if_fail (SYSPROF_IS_VISUALIZER (self)); - - if (g_strcmp0 (priv->title, title) != 0) - { - g_free (priv->title); - priv->title = g_strdup (title); - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_TITLE]); - } -} - -gint64 -sysprof_visualizer_get_begin_time (SysprofVisualizer *self) -{ - SysprofVisualizerPrivate *priv = sysprof_visualizer_get_instance_private (self); - - g_return_val_if_fail (SYSPROF_IS_VISUALIZER (self), 0); - - return priv->begin_time; -} - -gint64 -sysprof_visualizer_get_end_time (SysprofVisualizer *self) -{ - SysprofVisualizerPrivate *priv = sysprof_visualizer_get_instance_private (self); - - g_return_val_if_fail (SYSPROF_IS_VISUALIZER (self), 0); - - return priv->end_time; -} - -gint64 -sysprof_visualizer_get_duration (SysprofVisualizer *self) -{ - g_return_val_if_fail (SYSPROF_IS_VISUALIZER (self), 0); - - return sysprof_visualizer_get_end_time (self) - - sysprof_visualizer_get_begin_time (self); -} - -void -sysprof_visualizer_set_reader (SysprofVisualizer *self, - SysprofCaptureReader *reader) -{ - SysprofVisualizerPrivate *priv = sysprof_visualizer_get_instance_private (self); - - g_return_if_fail (SYSPROF_IS_VISUALIZER (self)); - g_return_if_fail (reader != NULL); - - if (priv->begin_time == 0 || priv->end_time == 0) - { - priv->begin_time = sysprof_capture_reader_get_start_time (reader); - priv->end_time = sysprof_capture_reader_get_end_time (reader); - priv->duration = priv->end_time - priv->begin_time; - } - - if (SYSPROF_VISUALIZER_GET_CLASS (self)->set_reader) - SYSPROF_VISUALIZER_GET_CLASS (self)->set_reader (self, reader); - - gtk_widget_queue_allocate (GTK_WIDGET (self)); -} - -void -sysprof_visualizer_translate_points (SysprofVisualizer *self, - const SysprofVisualizerRelativePoint *in_points, - guint n_in_points, - SysprofVisualizerAbsolutePoint *out_points, - guint n_out_points) -{ - int width; - int height; - - g_return_if_fail (SYSPROF_IS_VISUALIZER (self)); - g_return_if_fail (in_points != NULL); - g_return_if_fail (out_points != NULL); - g_return_if_fail (n_in_points == n_out_points); - - width = gtk_widget_get_width (GTK_WIDGET (self)); - height = gtk_widget_get_height (GTK_WIDGET (self)); - - for (guint i = 0; i < n_in_points; i++) - { - out_points[i].x = (in_points[i].x * width); - out_points[i].y = height - (ABS (in_points[i].y) * height); - } -} - -gint -sysprof_visualizer_get_x_for_time (SysprofVisualizer *self, - gint64 time) -{ - SysprofVisualizerPrivate *priv = sysprof_visualizer_get_instance_private (self); - - return ((time - priv->begin_time) / (gdouble)priv->duration) * gtk_widget_get_width (GTK_WIDGET (self)); -} - -void -sysprof_visualizer_set_time_range (SysprofVisualizer *self, - gint64 begin_time, - gint64 end_time) -{ - SysprofVisualizerPrivate *priv = sysprof_visualizer_get_instance_private (self); - - g_return_if_fail (SYSPROF_IS_VISUALIZER (self)); - - priv->begin_time = begin_time; - priv->end_time = end_time; - priv->duration = end_time - begin_time; - - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_BEGIN_TIME]); - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_END_TIME]); - - gtk_widget_queue_allocate (GTK_WIDGET (self)); -} diff --git a/src/libsysprof-ui/sysprof-visualizer.h b/src/libsysprof-ui/sysprof-visualizer.h deleted file mode 100644 index 74b8a31c..00000000 --- a/src/libsysprof-ui/sysprof-visualizer.h +++ /dev/null @@ -1,88 +0,0 @@ -/* sysprof-visualizer.h - * - * Copyright 2019 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 - -#if !defined (SYSPROF_UI_INSIDE) && !defined (SYSPROF_UI_COMPILATION) -# error "Only can be included directly." -#endif - -#include -#include - -G_BEGIN_DECLS - -typedef struct -{ - gdouble x; - gdouble y; -} SysprofVisualizerRelativePoint; - -typedef struct -{ - gint x; - gint y; -} SysprofVisualizerAbsolutePoint; - -#define SYSPROF_TYPE_VISUALIZER (sysprof_visualizer_get_type()) - -SYSPROF_AVAILABLE_IN_ALL -G_DECLARE_DERIVABLE_TYPE (SysprofVisualizer, sysprof_visualizer, SYSPROF, VISUALIZER, GtkWidget) - -struct _SysprofVisualizerClass -{ - GtkWidgetClass parent_class; - - void (*set_reader) (SysprofVisualizer *self, - SysprofCaptureReader *reader); - - /*< private >*/ - gpointer _reserved[16]; -}; - -SYSPROF_AVAILABLE_IN_ALL -const gchar *sysprof_visualizer_get_title (SysprofVisualizer *self); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_visualizer_set_title (SysprofVisualizer *self, - const gchar *title); -SYSPROF_AVAILABLE_IN_ALL -gint64 sysprof_visualizer_get_begin_time (SysprofVisualizer *self); -SYSPROF_AVAILABLE_IN_ALL -gint64 sysprof_visualizer_get_end_time (SysprofVisualizer *self); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_visualizer_set_time_range (SysprofVisualizer *self, - gint64 begin_time, - gint64 end_time); -SYSPROF_AVAILABLE_IN_ALL -gint64 sysprof_visualizer_get_duration (SysprofVisualizer *self); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_visualizer_set_reader (SysprofVisualizer *self, - SysprofCaptureReader *reader); -SYSPROF_AVAILABLE_IN_ALL -gint sysprof_visualizer_get_x_for_time (SysprofVisualizer *self, - gint64 time); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_visualizer_translate_points (SysprofVisualizer *self, - const SysprofVisualizerRelativePoint *in_points, - guint n_in_points, - SysprofVisualizerAbsolutePoint *out_points, - guint n_out_points); - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-visualizers-frame.c b/src/libsysprof-ui/sysprof-visualizers-frame.c deleted file mode 100644 index 33af9ce3..00000000 --- a/src/libsysprof-ui/sysprof-visualizers-frame.c +++ /dev/null @@ -1,721 +0,0 @@ -/* sysprof-visualizers-frame.c - * - * Copyright 2019 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 - */ - -#define G_LOG_DOMAIN "sysprof-visualizers-frame" - -#include "config.h" - -#include "sysprof-scrollmap.h" -#include "sysprof-visualizer-group-private.h" -#include "sysprof-visualizer-ticks.h" -#include "sysprof-visualizers-frame.h" -#include "sysprof-zoom-manager.h" - -struct _SysprofVisualizersFrame -{ - GtkWidget parent_instance; - - /* Drag selection tracking */ - SysprofSelection *selection; - gint64 drag_begin_at; - gint64 drag_selection_at; - guint button_pressed : 1; - - /* Help avoid over-resizing/allocating */ - GtkAllocation last_alloc; - gdouble last_zoom; - - /* Known time range from the capture */ - gint64 begin_time; - gint64 end_time; - - /* Template Widgets */ - GtkListBox *groups; - GtkListBox *visualizers; - SysprofScrollmap *hscrollbar; - SysprofVisualizerTicks *ticks; - GtkScrolledWindow *ticks_scroller; - GtkScrolledWindow *hscroller; - GtkScrolledWindow *vscroller; - SysprofZoomManager *zoom_manager; - GtkScale *zoom_scale; - GtkSizeGroup *left_column; - GtkViewport *ticks_viewport; - GtkViewport *visualizers_viewport; -}; - -typedef struct -{ - GtkListBox *list; - GtkStyleContext *style_context; - GtkSnapshot *snapshot; - int width; - int height; - gint64 begin_time; - gint64 duration; -} SelectionDraw; - -G_DEFINE_TYPE (SysprofVisualizersFrame, sysprof_visualizers_frame, GTK_TYPE_WIDGET) - -enum { - PROP_0, - PROP_SELECTED_GROUP, - PROP_SELECTION, - N_PROPS -}; - -static GParamSpec *properties [N_PROPS]; - -static gint64 -get_time_from_x (SysprofVisualizersFrame *self, - gdouble x) -{ - GtkAllocation alloc; - gdouble ratio; - gint64 duration; - - g_assert (SYSPROF_IS_VISUALIZERS_FRAME (self)); - - gtk_widget_get_allocation (GTK_WIDGET (self->ticks), &alloc); - duration = sysprof_visualizer_get_duration (SYSPROF_VISUALIZER (self->ticks)); - - if (alloc.width < 1) - return 0; - - ratio = x / alloc.width; - - return self->begin_time + (ratio * duration); -} - -static void -draw_selection_cb (SysprofSelection *selection, - gint64 range_begin, - gint64 range_end, - gpointer user_data) -{ - SelectionDraw *draw = user_data; - GdkRectangle area; - gdouble x, x2; - - g_assert (SYSPROF_IS_SELECTION (selection)); - g_assert (draw != NULL); - g_assert (draw->snapshot != NULL); - g_assert (GTK_IS_LIST_BOX (draw->list)); - - x = (range_begin - draw->begin_time) / (gdouble)draw->duration; - x2 = (range_end - draw->begin_time) / (gdouble)draw->duration; - - area.x = x * draw->width; - area.width = (x2 * draw->width) - area.x; - area.y = 0; - area.height = draw->height; - - if (area.width < 0) - { - area.width = ABS (area.width); - area.x -= area.width; - } - - gtk_snapshot_render_background (draw->snapshot, draw->style_context, area.x + 2, area.y + 2, area.width - 4, area.height - 4); -} - -static void -sysprof_visualizers_frame_snapshot (GtkWidget *widget, - GtkSnapshot *snapshot) -{ - SysprofVisualizersFrame *self = (SysprofVisualizersFrame *)widget; - SelectionDraw draw; - GtkAllocation alloc; - - g_assert (SYSPROF_IS_VISUALIZERS_FRAME (self)); - g_assert (GTK_IS_SNAPSHOT (snapshot)); - - GTK_WIDGET_CLASS (sysprof_visualizers_frame_parent_class)->snapshot (widget, snapshot); - - draw.duration = sysprof_visualizer_get_duration (SYSPROF_VISUALIZER (self->ticks)); - if (draw.duration == 0) - return; - - draw.style_context = gtk_widget_get_style_context (GTK_WIDGET (self->visualizers)); - draw.list = self->visualizers; - draw.snapshot = snapshot; - draw.begin_time = self->begin_time; - - gtk_widget_get_allocation (GTK_WIDGET (self->visualizers), &alloc); - draw.width = alloc.width; - draw.height = alloc.height; - - if (sysprof_selection_get_has_selection (self->selection) || self->button_pressed) - { - double x, y; - - gtk_snapshot_save (snapshot); - gtk_widget_translate_coordinates (GTK_WIDGET (self->visualizers), - GTK_WIDGET (self), - 0, 0, &x, &y); - gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (x, y)); - - gtk_style_context_add_class (draw.style_context, "selection"); - sysprof_selection_foreach (self->selection, draw_selection_cb, &draw); - if (self->button_pressed) - draw_selection_cb (self->selection, self->drag_begin_at, self->drag_selection_at, &draw); - gtk_style_context_remove_class (draw.style_context, "selection"); - - gtk_snapshot_restore (snapshot); - } -} - -static void -visualizers_button_press_event_cb (SysprofVisualizersFrame *self, - int n_presses, - double x, - double y, - GtkGestureClick *gesture) -{ - GdkModifierType state; - guint button; - - g_assert (SYSPROF_IS_VISUALIZERS_FRAME (self)); - g_assert (GTK_IS_GESTURE_CLICK (gesture)); - - button = gtk_gesture_single_get_button (GTK_GESTURE_SINGLE (gesture)); - - if (button != GDK_BUTTON_PRIMARY) - { - if (sysprof_selection_get_has_selection (self->selection)) - sysprof_selection_unselect_all (self->selection); - return; - } - - state = gtk_event_controller_get_current_event_state (GTK_EVENT_CONTROLLER (gesture)); - - if ((state & GDK_SHIFT_MASK) == 0) - sysprof_selection_unselect_all (self->selection); - - self->button_pressed = TRUE; - - self->drag_begin_at = get_time_from_x (self, x); - self->drag_selection_at = self->drag_begin_at; - - gtk_widget_queue_draw (GTK_WIDGET (self)); -} - -static void -visualizers_button_release_event_cb (SysprofVisualizersFrame *self, - int n_release, - double x, - double y, - GtkGestureClick *gesture) -{ - guint button; - - g_assert (SYSPROF_IS_VISUALIZERS_FRAME (self)); - g_assert (GTK_IS_GESTURE_CLICK (gesture)); - - button = gtk_gesture_single_get_button (GTK_GESTURE_SINGLE (gesture)); - - if (!self->button_pressed || button != GDK_BUTTON_PRIMARY) - return; - - self->button_pressed = FALSE; - - if (self->drag_begin_at != self->drag_selection_at) - { - sysprof_selection_select_range (self->selection, - self->drag_begin_at, - self->drag_selection_at); - self->drag_begin_at = -1; - self->drag_selection_at = -1; - } - - gtk_widget_queue_draw (GTK_WIDGET (self)); -} - -static void -visualizers_motion_notify_event_cb (SysprofVisualizersFrame *self, - double x, - double y, - GtkEventControllerMotion *motion) -{ - g_assert (SYSPROF_IS_VISUALIZERS_FRAME (self)); - g_assert (GTK_IS_EVENT_CONTROLLER_MOTION (motion)); - - if (self->button_pressed) - { - self->drag_selection_at = get_time_from_x (self, x); - gtk_widget_queue_draw (GTK_WIDGET (self)); - } -} - -static void -set_children_width_request (GtkWidget *widget, - int width) -{ - for (GtkWidget *child = gtk_widget_get_first_child (widget); - child; - child = gtk_widget_get_next_sibling (child)) - gtk_widget_set_size_request (child, width, -1); -} - -static void -sysprof_visualizers_frame_notify_zoom (SysprofVisualizersFrame *self, - GParamSpec *pspec, - SysprofZoomManager *zoom_manager) -{ - gint64 duration; - gint data_width; - - g_assert (SYSPROF_IS_VISUALIZERS_FRAME (self)); - g_assert (SYSPROF_IS_ZOOM_MANAGER (zoom_manager)); - - duration = self->end_time - self->begin_time; - data_width = sysprof_zoom_manager_get_width_for_duration (self->zoom_manager, duration); - set_children_width_request (GTK_WIDGET (self->ticks_viewport), data_width); - set_children_width_request (GTK_WIDGET (self->visualizers_viewport), data_width); -} - -static gint -find_pos (SysprofVisualizersFrame *self, - const gchar *title, - gint priority) -{ - gint pos = 0; - - if (title == NULL) - return -1; - - for (GtkWidget *child = gtk_widget_get_first_child (GTK_WIDGET (self->visualizers)); - child; - child = gtk_widget_get_next_sibling (child)) - { - SysprofVisualizerGroup *group = SYSPROF_VISUALIZER_GROUP (child); - gint prio = sysprof_visualizer_group_get_priority (group); - const gchar *item = sysprof_visualizer_group_get_title (group); - - if (priority < prio || - (priority == prio && g_strcmp0 (title, item) < 0)) - break; - - pos++; - } - - return pos; -} - -void -sysprof_visualizers_frame_add_group (SysprofVisualizersFrame *self, - SysprofVisualizerGroup *group) -{ - SysprofVisualizerGroupHeader *header; - const char *title; - int priority; - int pos; - - g_return_if_fail (SYSPROF_IS_VISUALIZERS_FRAME (self)); - g_return_if_fail (SYSPROF_IS_VISUALIZER_GROUP (group)); - - title = sysprof_visualizer_group_get_title (group); - priority = sysprof_visualizer_group_get_priority (group); - pos = find_pos (self, title, priority); - - gtk_list_box_insert (self->visualizers, GTK_WIDGET (group), pos); - - header = _sysprof_visualizer_group_header_new (group); - gtk_list_box_insert (self->groups, GTK_WIDGET (header), pos); - _sysprof_visualizer_group_set_header (group, header); - gtk_widget_show (GTK_WIDGET (header)); - - sysprof_visualizers_frame_notify_zoom (self, NULL, self->zoom_manager); -} - -static void -sysprof_visualizers_frame_selection_changed (SysprofVisualizersFrame *self, - SysprofSelection *selection) -{ - g_assert (SYSPROF_IS_VISUALIZERS_FRAME (self)); - g_assert (SYSPROF_IS_SELECTION (selection)); - - gtk_widget_queue_draw (GTK_WIDGET (self->visualizers)); - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_SELECTION]); -} - -static void -sysprof_visualizers_frame_group_activated_cb (SysprofVisualizersFrame *self, - SysprofVisualizerGroupHeader *row, - GtkListBox *list) -{ - SysprofVisualizerGroup *group; - - g_assert (SYSPROF_IS_VISUALIZERS_FRAME (self)); - g_assert (SYSPROF_IS_VISUALIZER_GROUP_HEADER (row)); - - group = _sysprof_visualizer_group_header_get_group (row); - g_assert (SYSPROF_IS_VISUALIZER_GROUP (group)); - - g_signal_emit_by_name (group, "group-activated"); -} - -static void -sysprof_visualizers_frame_dispose (GObject *object) -{ - SysprofVisualizersFrame *self = (SysprofVisualizersFrame *)object; - GtkWidget *child; - - while ((child = gtk_widget_get_first_child (GTK_WIDGET (self)))) - gtk_widget_unparent (child); - - g_clear_object (&self->selection); - - G_OBJECT_CLASS (sysprof_visualizers_frame_parent_class)->dispose (object); -} - -static void -sysprof_visualizers_frame_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - SysprofVisualizersFrame *self = SYSPROF_VISUALIZERS_FRAME (object); - - switch (prop_id) - { - case PROP_SELECTED_GROUP: - g_value_set_object (value, sysprof_visualizers_frame_get_selected_group (self)); - break; - - case PROP_SELECTION: - g_value_set_object (value, sysprof_visualizers_frame_get_selection (self)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_visualizers_frame_class_init (SysprofVisualizersFrameClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); - - object_class->dispose = sysprof_visualizers_frame_dispose; - object_class->get_property = sysprof_visualizers_frame_get_property; - - widget_class->snapshot = sysprof_visualizers_frame_snapshot; - - gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/sysprof/ui/sysprof-visualizers-frame.ui"); - gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT); - gtk_widget_class_set_css_name (widget_class, "SysprofVisualizersFrame"); - gtk_widget_class_bind_template_child (widget_class, SysprofVisualizersFrame, groups); - gtk_widget_class_bind_template_child (widget_class, SysprofVisualizersFrame, hscrollbar); - gtk_widget_class_bind_template_child (widget_class, SysprofVisualizersFrame, hscroller); - gtk_widget_class_bind_template_child (widget_class, SysprofVisualizersFrame, left_column); - gtk_widget_class_bind_template_child (widget_class, SysprofVisualizersFrame, ticks); - gtk_widget_class_bind_template_child (widget_class, SysprofVisualizersFrame, ticks_scroller); - gtk_widget_class_bind_template_child (widget_class, SysprofVisualizersFrame, visualizers); - gtk_widget_class_bind_template_child (widget_class, SysprofVisualizersFrame, vscroller); - gtk_widget_class_bind_template_child (widget_class, SysprofVisualizersFrame, zoom_manager); - gtk_widget_class_bind_template_child (widget_class, SysprofVisualizersFrame, zoom_scale); - gtk_widget_class_bind_template_child (widget_class, SysprofVisualizersFrame, ticks_viewport); - gtk_widget_class_bind_template_child (widget_class, SysprofVisualizersFrame, visualizers_viewport); - - properties [PROP_SELECTED_GROUP] = - g_param_spec_object ("selected-group", - "Selected Group", - "The selected group", - SYSPROF_TYPE_VISUALIZER_GROUP, - (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - - properties [PROP_SELECTION] = - g_param_spec_object ("selection", - "Selection", - "The time selection", - SYSPROF_TYPE_SELECTION, - (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_properties (object_class, N_PROPS, properties); - - g_type_ensure (SYSPROF_TYPE_SCROLLMAP); - g_type_ensure (SYSPROF_TYPE_VISUALIZER_TICKS); - g_type_ensure (SYSPROF_TYPE_ZOOM_MANAGER); -} - -static void -sysprof_visualizers_frame_init (SysprofVisualizersFrame *self) -{ - GtkEventController *controller; - GtkAdjustment *hadj; - GtkAdjustment *zadj; - - gtk_widget_init_template (GTK_WIDGET (self)); - - gtk_widget_set_cursor_from_name (GTK_WIDGET (self->visualizers), "text"); - - controller = GTK_EVENT_CONTROLLER (gtk_gesture_click_new ()); - g_signal_connect_object (controller, - "pressed", - G_CALLBACK (visualizers_button_press_event_cb), - self, - G_CONNECT_SWAPPED); - g_signal_connect_object (controller, - "released", - G_CALLBACK (visualizers_button_release_event_cb), - self, - G_CONNECT_SWAPPED); - gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_CAPTURE); - gtk_widget_add_controller (GTK_WIDGET (self->visualizers), controller); - - controller = gtk_event_controller_motion_new (); - g_signal_connect_object (controller, - "motion", - G_CALLBACK (visualizers_motion_notify_event_cb), - self, - G_CONNECT_SWAPPED); - gtk_widget_add_controller (GTK_WIDGET (self->visualizers), controller); - - self->selection = g_object_new (SYSPROF_TYPE_SELECTION, NULL); - - zadj = sysprof_zoom_manager_get_adjustment (self->zoom_manager); - hadj = gtk_scrolled_window_get_hadjustment (self->hscroller); - - gtk_scrolled_window_set_hadjustment (self->ticks_scroller, hadj); - sysprof_scrollmap_set_adjustment (self->hscrollbar, hadj); - gtk_range_set_adjustment (GTK_RANGE (self->zoom_scale), zadj); - - gtk_widget_insert_action_group (GTK_WIDGET (self), - "zoom", - G_ACTION_GROUP (self->zoom_manager)); - - g_signal_connect_object (self->groups, - "row-activated", - G_CALLBACK (sysprof_visualizers_frame_group_activated_cb), - self, - G_CONNECT_SWAPPED); - - g_signal_connect_object (self->selection, - "changed", - G_CALLBACK (sysprof_visualizers_frame_selection_changed), - self, - G_CONNECT_SWAPPED); - - g_signal_connect_object (self->zoom_manager, - "notify::zoom", - G_CALLBACK (sysprof_visualizers_frame_notify_zoom), - self, - G_CONNECT_SWAPPED | G_CONNECT_AFTER); -} - -/** - * sysprof_visualizers_frame_get_selected_group: - * - * Gets the currently selected group. - * - * Returns: (transfer none) (nullable): the selected row - * - * Since: 3.34 - */ -SysprofVisualizerGroup * -sysprof_visualizers_frame_get_selected_group (SysprofVisualizersFrame *self) -{ - GtkListBoxRow *row; - - g_return_val_if_fail (SYSPROF_IS_VISUALIZERS_FRAME (self), NULL); - - row = gtk_list_box_get_selected_row (self->groups); - - return SYSPROF_VISUALIZER_GROUP (row); -} - -/** - * sysprof_visualizers_frame_get_selection: - * - * Get the time selection - * - * Returns: (transfer none): a #SysprofSelection - * - * Since: 3.34 - */ -SysprofSelection * -sysprof_visualizers_frame_get_selection (SysprofVisualizersFrame *self) -{ - g_return_val_if_fail (SYSPROF_IS_VISUALIZERS_FRAME (self), NULL); - - return self->selection; -} - -static gint -compare_gint64 (const gint64 *a, - const gint64 *b) -{ - if (*a < *b) - return -1; - else if (*a > *b) - return 1; - else - return 0; -} - -static bool -index_frame_times_frame_cb (const SysprofCaptureFrame *frame, - gpointer user_data) -{ - GArray *array = user_data; - - /* Track timing, but ignore some common types at startup */ - if (frame->type != SYSPROF_CAPTURE_FRAME_MAP && - frame->type != SYSPROF_CAPTURE_FRAME_PROCESS) - g_array_append_val (array, frame->time); - - return TRUE; -} - -static void -index_frame_times_worker (GTask *task, - gpointer source_object, - gpointer task_data, - GCancellable *cancellable) -{ - SysprofCaptureCursor *cursor = task_data; - GArray *timings = NULL; - - g_assert (G_IS_TASK (task)); - g_assert (SYSPROF_IS_VISUALIZERS_FRAME (source_object)); - g_assert (cursor != NULL); - g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); - - timings = g_array_new (FALSE, FALSE, sizeof (gint64)); - sysprof_capture_cursor_foreach (cursor, index_frame_times_frame_cb, timings); - g_array_sort (timings, (GCompareFunc) compare_gint64); - - g_task_return_pointer (task, - g_steal_pointer (&timings), - (GDestroyNotify) g_array_unref); -} - -void -sysprof_visualizers_frame_load_async (SysprofVisualizersFrame *self, - SysprofCaptureReader *reader, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - g_autoptr(GTask) task = NULL; - GtkAllocation alloc; - - g_return_if_fail (SYSPROF_IS_VISUALIZERS_FRAME (self)); - g_return_if_fail (reader != NULL); - g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); - - gtk_widget_get_allocation (GTK_WIDGET (self->ticks), &alloc); - - /* At this point, the SysprofDisplay should have already scanned the - * reader for all of the events and therefore we can trust the begin - * and end time for the capture. - */ - self->begin_time = sysprof_capture_reader_get_start_time (reader); - self->end_time = sysprof_capture_reader_get_end_time (reader); - - /* Now we need to run through the frames and index their times - * so that we can calculate the number of items per bucket when - * drawing the scrollbar. - */ - task = g_task_new (self, cancellable, callback, user_data); - g_task_set_source_tag (task, sysprof_visualizers_frame_load_async); - g_task_set_task_data (task, - sysprof_capture_cursor_new (reader), - (GDestroyNotify) sysprof_capture_cursor_unref); - g_task_run_in_thread (task, index_frame_times_worker); -} - -gboolean -sysprof_visualizers_frame_load_finish (SysprofVisualizersFrame *self, - GAsyncResult *result, - GError **error) -{ - g_autoptr(GArray) timings = NULL; - - g_return_val_if_fail (SYSPROF_IS_VISUALIZERS_FRAME (self), FALSE); - g_return_val_if_fail (G_IS_TASK (result), FALSE); - - if ((timings = g_task_propagate_pointer (G_TASK (result), error))) - { - sysprof_scrollmap_set_timings (self->hscrollbar, timings); - sysprof_scrollmap_set_time_range (self->hscrollbar, self->begin_time, self->end_time); - sysprof_visualizer_set_time_range (SYSPROF_VISUALIZER (self->ticks), self->begin_time, self->end_time); - gtk_widget_queue_resize (GTK_WIDGET (self)); - - return TRUE; - } - - return FALSE; -} - -/** - * sysprof_visualizers_frame_get_zoom_manager: - * - * Gets the zoom manager for the frame. - * - * Returns: (transfer none): a #SysprofZoomManager - */ -SysprofZoomManager * -sysprof_visualizers_frame_get_zoom_manager (SysprofVisualizersFrame *self) -{ - g_return_val_if_fail (SYSPROF_IS_VISUALIZERS_FRAME (self), NULL); - - return self->zoom_manager; -} - -/** - * sysprof_visualizers_frame_get_size_group: - * - * gets the left column size group. - * - * Returns: (transfer none): a size group - */ -GtkSizeGroup * -sysprof_visualizers_frame_get_size_group (SysprofVisualizersFrame *self) -{ - g_return_val_if_fail (SYSPROF_IS_VISUALIZERS_FRAME (self), NULL); - - return self->left_column; -} - -/** - * sysprof_visualizers_frame_get_hadjustment: - * - * Gets the scroll adjustment used for horizontal scrolling - * - * Returns: (transfer none): a #GtkAdjustment - */ -GtkAdjustment * -sysprof_visualizers_frame_get_hadjustment (SysprofVisualizersFrame *self) -{ - g_return_val_if_fail (SYSPROF_IS_VISUALIZERS_FRAME (self), NULL); - - return sysprof_scrollmap_get_adjustment (self->hscrollbar); -} - -void -sysprof_visualizers_frame_unselect_row (SysprofVisualizersFrame *self) -{ - g_return_if_fail (SYSPROF_IS_VISUALIZERS_FRAME (self)); - - gtk_list_box_unselect_all (self->groups); -} diff --git a/src/libsysprof-ui/sysprof-visualizers-frame.h b/src/libsysprof-ui/sysprof-visualizers-frame.h deleted file mode 100644 index 3217ab57..00000000 --- a/src/libsysprof-ui/sysprof-visualizers-frame.h +++ /dev/null @@ -1,52 +0,0 @@ -/* sysprof-visualizers-frame.h - * - * Copyright 2019 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 - -#include "sysprof-visualizer-group.h" -#include "sysprof-zoom-manager.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_VISUALIZERS_FRAME (sysprof_visualizers_frame_get_type()) - -G_DECLARE_FINAL_TYPE (SysprofVisualizersFrame, sysprof_visualizers_frame, SYSPROF, VISUALIZERS_FRAME, GtkWidget) - -void sysprof_visualizers_frame_add_group (SysprofVisualizersFrame *self, - SysprofVisualizerGroup *group); -SysprofSelection *sysprof_visualizers_frame_get_selection (SysprofVisualizersFrame *self); -SysprofVisualizerGroup *sysprof_visualizers_frame_get_selected_group (SysprofVisualizersFrame *self); -SysprofZoomManager *sysprof_visualizers_frame_get_zoom_manager (SysprofVisualizersFrame *self); -void sysprof_visualizers_frame_load_async (SysprofVisualizersFrame *self, - SysprofCaptureReader *reader, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean sysprof_visualizers_frame_load_finish (SysprofVisualizersFrame *self, - GAsyncResult *result, - GError **error); -GtkSizeGroup *sysprof_visualizers_frame_get_size_group (SysprofVisualizersFrame *self); -GtkAdjustment *sysprof_visualizers_frame_get_hadjustment (SysprofVisualizersFrame *self); -void sysprof_visualizers_frame_unselect_row (SysprofVisualizersFrame *self); - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-visualizers-frame.ui b/src/libsysprof-ui/sysprof-visualizers-frame.ui deleted file mode 100644 index 4ff60076..00000000 --- a/src/libsysprof-ui/sysprof-visualizers-frame.ui +++ /dev/null @@ -1,242 +0,0 @@ - - - - - - - - - - - - vertical - - - - - - - - - - - - - diff --git a/src/libsysprof-ui/sysprof-zoom-manager.c b/src/libsysprof-ui/sysprof-zoom-manager.c deleted file mode 100644 index 679205e0..00000000 --- a/src/libsysprof-ui/sysprof-zoom-manager.c +++ /dev/null @@ -1,661 +0,0 @@ -/* sysprof-zoom-manager.c - * - * Copyright 2016-2019 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 - */ - -#define G_LOG_DOMAIN "sysprof-zoom-manager" - -#include "config.h" - -#include -#include -#include -#include - -#include "sysprof-zoom-manager.h" - -#define DEFAULT_PIXELS_PER_SEC (20.0) - -struct _SysprofZoomManager -{ - GObject parent_instance; - - GtkAdjustment *adjustment; - GSimpleActionGroup *actions; - - gdouble min_zoom; - gdouble max_zoom; - gdouble zoom; -} __attribute__((aligned(8))); - -enum { - PROP_0, - PROP_CAN_ZOOM_IN, - PROP_CAN_ZOOM_OUT, - PROP_MIN_ZOOM, - PROP_MAX_ZOOM, - PROP_ZOOM, - PROP_ZOOM_LABEL, - N_PROPS -}; - -static void action_group_iface_init (GActionGroupInterface *iface); - -G_DEFINE_TYPE_EXTENDED (SysprofZoomManager, sysprof_zoom_manager, G_TYPE_OBJECT, 0, - G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_GROUP, action_group_iface_init)) - -static GParamSpec *properties [N_PROPS]; -static gdouble zoom_levels[] = { - 0.3, 0.5, 0.67, 0.80, 0.90, - 1.0, 1.5, 2.0, 2.5, 3.0, - 5.0, 10.0, 20.0, 30.0, 50.0, -}; - -static void -sysprof_zoom_manager_zoom_in_action (GSimpleAction *action, - GVariant *param, - gpointer user_data) -{ - SysprofZoomManager *self = user_data; - g_assert (SYSPROF_IS_ZOOM_MANAGER (self)); - sysprof_zoom_manager_zoom_in (self); -} - -static void -sysprof_zoom_manager_zoom_out_action (GSimpleAction *action, - GVariant *param, - gpointer user_data) -{ - SysprofZoomManager *self = user_data; - g_assert (SYSPROF_IS_ZOOM_MANAGER (self)); - sysprof_zoom_manager_zoom_out (self); -} - -static void -sysprof_zoom_manager_zoom_one_action (GSimpleAction *action, - GVariant *param, - gpointer user_data) -{ - SysprofZoomManager *self = user_data; - g_assert (SYSPROF_IS_ZOOM_MANAGER (self)); - sysprof_zoom_manager_reset (self); -} - -static void -sysprof_zoom_manager_zoom_action (GSimpleAction *action, - GVariant *param, - gpointer user_data) -{ - SysprofZoomManager *self = user_data; - - g_assert (SYSPROF_IS_ZOOM_MANAGER (self)); - g_assert (g_variant_is_of_type (param, G_VARIANT_TYPE_DOUBLE)); - - sysprof_zoom_manager_set_zoom (self, g_variant_get_double (param)); -} - -static void -sysprof_zoom_manager_value_changed_cb (SysprofZoomManager *self, - GtkAdjustment *adjustment) -{ - gdouble value; - - g_assert (SYSPROF_IS_ZOOM_MANAGER (self)); - g_assert (GTK_IS_ADJUSTMENT (adjustment)); - - value = gtk_adjustment_get_value (adjustment); - - if (value == 0.0) - value = 1.0; - else if (value > 0.0) - value = (value + 1.0) * (value + 1.0); - else - value = 1.0 / ABS (value); - - sysprof_zoom_manager_set_zoom (self, value); -} - -static void -sysprof_zoom_manager_finalize (GObject *object) -{ - SysprofZoomManager *self = (SysprofZoomManager *)object; - - g_clear_object (&self->actions); - g_clear_object (&self->adjustment); - - G_OBJECT_CLASS (sysprof_zoom_manager_parent_class)->finalize (object); -} - -static void -sysprof_zoom_manager_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - SysprofZoomManager *self = SYSPROF_ZOOM_MANAGER (object); - - switch (prop_id) - { - case PROP_MIN_ZOOM: - g_value_set_double (value, sysprof_zoom_manager_get_min_zoom (self)); - break; - - case PROP_MAX_ZOOM: - g_value_set_double (value, sysprof_zoom_manager_get_max_zoom (self)); - break; - - case PROP_ZOOM: - g_value_set_double (value, sysprof_zoom_manager_get_zoom (self)); - break; - - case PROP_ZOOM_LABEL: - g_value_take_string (value, sysprof_zoom_manager_get_zoom_label (self)); - break; - - case PROP_CAN_ZOOM_IN: - g_value_set_boolean (value, sysprof_zoom_manager_get_can_zoom_in (self)); - break; - - case PROP_CAN_ZOOM_OUT: - g_value_set_boolean (value, sysprof_zoom_manager_get_can_zoom_out (self)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_zoom_manager_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - SysprofZoomManager *self = SYSPROF_ZOOM_MANAGER (object); - - switch (prop_id) - { - case PROP_MIN_ZOOM: - sysprof_zoom_manager_set_min_zoom (self, g_value_get_double (value)); - break; - - case PROP_MAX_ZOOM: - sysprof_zoom_manager_set_max_zoom (self, g_value_get_double (value)); - break; - - case PROP_ZOOM: - sysprof_zoom_manager_set_zoom (self, g_value_get_double (value)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_zoom_manager_class_init (SysprofZoomManagerClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->finalize = sysprof_zoom_manager_finalize; - object_class->get_property = sysprof_zoom_manager_get_property; - object_class->set_property = sysprof_zoom_manager_set_property; - - properties [PROP_CAN_ZOOM_IN] = - g_param_spec_boolean ("can-zoom-in", - "Can Zoom In", - "Can Zoom In", - TRUE, - (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - - properties [PROP_CAN_ZOOM_OUT] = - g_param_spec_boolean ("can-zoom-out", - "Can Zoom Out", - "Can Zoom Out", - TRUE, - (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - - properties [PROP_MIN_ZOOM] = - g_param_spec_double ("min-zoom", - "Min Zoom", - "The minimum zoom to apply", - -G_MAXDOUBLE, - G_MAXDOUBLE, - 0.0, - (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - properties [PROP_MAX_ZOOM] = - g_param_spec_double ("max-zoom", - "Max Zoom", - "The maximum zoom to apply", - -G_MAXDOUBLE, - G_MAXDOUBLE, - 0.0, - (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - properties [PROP_ZOOM] = - g_param_spec_double ("zoom", - "Zoom", - "The current zoom level", - -G_MAXDOUBLE, - G_MAXDOUBLE, - 1.0, - (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - properties [PROP_ZOOM_LABEL] = - g_param_spec_string ("zoom-label", NULL, NULL, - NULL, - (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_properties (object_class, N_PROPS, properties); -} - -static void -sysprof_zoom_manager_init (SysprofZoomManager *self) -{ - static const GActionEntry entries[] = { - { "zoom-in", sysprof_zoom_manager_zoom_in_action }, - { "zoom-out", sysprof_zoom_manager_zoom_out_action }, - { "zoom-one", sysprof_zoom_manager_zoom_one_action }, - { "zoom", NULL, "d", "1.0", sysprof_zoom_manager_zoom_action }, - }; - GAction *action; - - self->min_zoom = 0.00001; - self->max_zoom = 10000.0; - self->zoom = 1.0; - - self->adjustment = g_object_ref_sink (gtk_adjustment_new (0, -10, 10, 1, 3, 0)); - - g_signal_connect_object (self->adjustment, - "value-changed", - G_CALLBACK (sysprof_zoom_manager_value_changed_cb), - self, - G_CONNECT_SWAPPED); - - self->actions = g_simple_action_group_new (); - g_action_map_add_action_entries (G_ACTION_MAP (self->actions), - entries, - G_N_ELEMENTS (entries), - self); - - action = g_action_map_lookup_action (G_ACTION_MAP (self->actions), "zoom-in"); - g_object_bind_property (self, "can-zoom-in", action, "enabled", G_BINDING_SYNC_CREATE); - - action = g_action_map_lookup_action (G_ACTION_MAP (self->actions), "zoom-out"); - g_object_bind_property (self, "can-zoom-out", action, "enabled", G_BINDING_SYNC_CREATE); -} - -SysprofZoomManager * -sysprof_zoom_manager_new (void) -{ - return g_object_new (SYSPROF_TYPE_ZOOM_MANAGER, NULL); -} - -gboolean -sysprof_zoom_manager_get_can_zoom_in (SysprofZoomManager *self) -{ - g_return_val_if_fail (SYSPROF_IS_ZOOM_MANAGER (self), FALSE); - - return self->max_zoom == 0.0 || self->zoom < self->max_zoom; -} - -gboolean -sysprof_zoom_manager_get_can_zoom_out (SysprofZoomManager *self) -{ - g_return_val_if_fail (SYSPROF_IS_ZOOM_MANAGER (self), FALSE); - - return self->min_zoom == 0.0 || self->zoom > self->min_zoom; -} - -gboolean -sysprof_zoom_manager_get_min_zoom (SysprofZoomManager *self) -{ - g_return_val_if_fail (SYSPROF_IS_ZOOM_MANAGER (self), FALSE); - - return self->min_zoom; -} - -gboolean -sysprof_zoom_manager_get_max_zoom (SysprofZoomManager *self) -{ - g_return_val_if_fail (SYSPROF_IS_ZOOM_MANAGER (self), FALSE); - - return self->max_zoom; -} - -void -sysprof_zoom_manager_set_min_zoom (SysprofZoomManager *self, - gdouble min_zoom) -{ - g_return_if_fail (SYSPROF_IS_ZOOM_MANAGER (self)); - - if (min_zoom != self->min_zoom) - { - self->min_zoom = min_zoom; - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_MIN_ZOOM]); - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CAN_ZOOM_OUT]); - } -} - -void -sysprof_zoom_manager_set_max_zoom (SysprofZoomManager *self, - gdouble max_zoom) -{ - g_return_if_fail (SYSPROF_IS_ZOOM_MANAGER (self)); - - if (max_zoom != self->max_zoom) - { - self->max_zoom = max_zoom; - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_MAX_ZOOM]); - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CAN_ZOOM_IN]); - } -} - -void -sysprof_zoom_manager_zoom_in (SysprofZoomManager *self) -{ - gdouble zoom; - - g_return_if_fail (SYSPROF_IS_ZOOM_MANAGER (self)); - - if (!sysprof_zoom_manager_get_can_zoom_in (self)) - return; - - zoom = self->zoom; - - for (guint i = 0; i < G_N_ELEMENTS (zoom_levels); i++) - { - if (zoom_levels[i] > zoom) - { - zoom = zoom_levels[i]; - break; - } - } - - if (zoom == self->zoom) - zoom *= 2; - - sysprof_zoom_manager_set_zoom (self, zoom); -} - -void -sysprof_zoom_manager_zoom_out (SysprofZoomManager *self) -{ - gdouble zoom; - - g_return_if_fail (SYSPROF_IS_ZOOM_MANAGER (self)); - - if (!sysprof_zoom_manager_get_can_zoom_out (self)) - return; - - zoom = self->zoom; - - for (guint i = G_N_ELEMENTS (zoom_levels); i > 0; i--) - { - if (zoom_levels[i-1] < zoom) - { - zoom = zoom_levels[i-1]; - break; - } - } - - if (zoom == self->zoom) - zoom /= 2.0; - - sysprof_zoom_manager_set_zoom (self, zoom); -} - -void -sysprof_zoom_manager_reset (SysprofZoomManager *self) -{ - g_return_if_fail (SYSPROF_IS_ZOOM_MANAGER (self)); - - sysprof_zoom_manager_set_zoom (self, 1.0); -} - -gdouble -sysprof_zoom_manager_get_zoom (SysprofZoomManager *self) -{ - g_return_val_if_fail (SYSPROF_IS_ZOOM_MANAGER (self), 0.0); - - return self->zoom; -} - -void -sysprof_zoom_manager_set_zoom (SysprofZoomManager *self, - gdouble zoom) -{ - gdouble min_zoom; - gdouble max_zoom; - - g_return_if_fail (SYSPROF_IS_ZOOM_MANAGER (self)); - - min_zoom = (self->min_zoom == 0.0) ? -G_MAXDOUBLE : self->min_zoom; - max_zoom = (self->max_zoom == 0.0) ? G_MAXDOUBLE : self->max_zoom; - - zoom = CLAMP (zoom, min_zoom, max_zoom); - - if (zoom == 0.0) - zoom = 1.0; - - if (zoom != self->zoom) - { - g_autoptr(GVariant) state = NULL; - gdouble adj; - - self->zoom = zoom; - state = g_variant_take_ref (g_variant_new_double (zoom)); - - g_object_set (g_action_map_lookup_action (G_ACTION_MAP (self->actions), "zoom"), - "state", state, - NULL); - - /* Convert to form used by adjustment */ - if (zoom == 1.0) - adj = 0.0; - else if (zoom > 1.0) - adj = sqrt (zoom) - 1.0; - else - adj = -1 / zoom; - - g_signal_handlers_block_matched (self->adjustment, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, - sysprof_zoom_manager_value_changed_cb, self); - gtk_adjustment_set_value (self->adjustment, adj); - g_signal_handlers_unblock_matched (self->adjustment, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, - sysprof_zoom_manager_value_changed_cb, self); - - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ZOOM]); - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CAN_ZOOM_IN]); - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CAN_ZOOM_OUT]); - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ZOOM_LABEL]); - } -} - -static gchar ** -sysprof_zoom_manager_list_actions (GActionGroup *action_group) -{ - SysprofZoomManager *self = (SysprofZoomManager *)action_group; - - g_assert (SYSPROF_IS_ZOOM_MANAGER (self)); - - return g_action_group_list_actions (G_ACTION_GROUP (self->actions)); -} - -static gboolean -sysprof_zoom_manager_query_action (GActionGroup *action_group, - const gchar *action_name, - gboolean *enabled, - const GVariantType **parameter_type, - const GVariantType **state_type, - GVariant **state_hint, - GVariant **state) -{ - SysprofZoomManager *self = (SysprofZoomManager *)action_group; - - g_assert (SYSPROF_IS_ZOOM_MANAGER (self)); - g_assert (action_name != NULL); - - return g_action_group_query_action (G_ACTION_GROUP (self->actions), - action_name, - enabled, - parameter_type, - state_type, - state_hint, - state); -} - -static void -sysprof_zoom_manager_change_action_state (GActionGroup *action_group, - const gchar *action_name, - GVariant *value) -{ - SysprofZoomManager *self = (SysprofZoomManager *)action_group; - - g_assert (SYSPROF_IS_ZOOM_MANAGER (self)); - g_assert (action_name != NULL); - - g_action_group_change_action_state (G_ACTION_GROUP (self->actions), action_name, value); -} - -static void -sysprof_zoom_manager_activate_action (GActionGroup *action_group, - const gchar *action_name, - GVariant *parameter) -{ - SysprofZoomManager *self = (SysprofZoomManager *)action_group; - - g_assert (SYSPROF_IS_ZOOM_MANAGER (self)); - g_assert (action_name != NULL); - - g_action_group_activate_action (G_ACTION_GROUP (self->actions), action_name, parameter); -} - -static void -action_group_iface_init (GActionGroupInterface *iface) -{ - iface->list_actions = sysprof_zoom_manager_list_actions; - iface->query_action = sysprof_zoom_manager_query_action; - iface->change_action_state = sysprof_zoom_manager_change_action_state; - iface->activate_action = sysprof_zoom_manager_activate_action; -} - -/** - * sysprof_zoom_manager_get_zoom_level: - * @self: a #SysprofZoomManager - * - * Returns: (transfer full): a string containing the zoom percentage label - * - * Since: 3.34 - */ -gchar * -sysprof_zoom_manager_get_zoom_label (SysprofZoomManager *self) -{ - gdouble percent; - - g_return_val_if_fail (SYSPROF_IS_ZOOM_MANAGER (self), NULL); - - percent = self->zoom * 100.0; - - if (percent < 1.0) - return g_strdup_printf ("%0.2lf%%", percent); - else - return g_strdup_printf ("%d%%", (gint)percent); -} - -gint64 -sysprof_zoom_manager_get_duration_for_width (SysprofZoomManager *self, - gint width) -{ - g_return_val_if_fail (SYSPROF_IS_ZOOM_MANAGER (self), 0); - - return SYSPROF_NSEC_PER_SEC * ((gdouble)width / (DEFAULT_PIXELS_PER_SEC * self->zoom)); -} - -gint -sysprof_zoom_manager_get_width_for_duration (SysprofZoomManager *self, - gint64 duration) -{ - g_return_val_if_fail (SYSPROF_IS_ZOOM_MANAGER (self), 0); - - return (gdouble)duration / (gdouble)SYSPROF_NSEC_PER_SEC * DEFAULT_PIXELS_PER_SEC * self->zoom; -} - -gdouble -sysprof_zoom_manager_fit_zoom_for_duration (SysprofZoomManager *self, - gint64 duration, - gint width) -{ - g_return_val_if_fail (SYSPROF_IS_ZOOM_MANAGER (self), 1.0); - g_return_val_if_fail (duration >= 0, 1.0); - g_return_val_if_fail (width >= 0, 1.0); - - return (width / DEFAULT_PIXELS_PER_SEC) / (duration / (gdouble)SYSPROF_NSEC_PER_SEC); -} - -gdouble -sysprof_zoom_manager_get_offset_at_time (SysprofZoomManager *self, - gint64 offset, - gint width) -{ - gint64 full_duration; - gdouble ratio; - - g_return_val_if_fail (SYSPROF_IS_ZOOM_MANAGER (self), 0.0); - - full_duration = sysprof_zoom_manager_get_duration_for_width (self, width); - ratio = offset / (gdouble)full_duration; - return ratio * width; -} - -gchar * -_sysprof_format_duration (gint64 duration) -{ - gboolean negative = duration < 0; - - if (duration == 0) - return g_strdup ("0"); - - duration = ABS (duration); - - if (duration < SYSPROF_NSEC_PER_SEC) - return g_strdup_printf ("%s%.3lf msec", - negative ? "-" : "", - (duration / (gdouble)SYSPROF_NSEC_PER_SEC * 1000L)); - else - return g_strdup_printf ("%s%.4lf seconds", - negative ? "-" : "", - (duration / (gdouble)SYSPROF_NSEC_PER_SEC)); -} - -/** - * sysprof_zoom_manager_get_adjustment: - * - * Gets an adjustment for zoom controls. - * - * Returns: (transfer none): a #GtkAdjustment - * - * Since: 3.34 - */ -GtkAdjustment * -sysprof_zoom_manager_get_adjustment (SysprofZoomManager *self) -{ - g_return_val_if_fail (SYSPROF_IS_ZOOM_MANAGER (self), NULL); - - return self->adjustment; -} diff --git a/src/libsysprof-ui/sysprof-zoom-manager.h b/src/libsysprof-ui/sysprof-zoom-manager.h deleted file mode 100644 index d25742f0..00000000 --- a/src/libsysprof-ui/sysprof-zoom-manager.h +++ /dev/null @@ -1,59 +0,0 @@ -/* sysprof-zoom-manager.h - * - * Copyright 2016-2019 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_ZOOM_MANAGER (sysprof_zoom_manager_get_type()) - -G_DECLARE_FINAL_TYPE (SysprofZoomManager, sysprof_zoom_manager, SYSPROF, ZOOM_MANAGER, GObject) - -SysprofZoomManager *sysprof_zoom_manager_new (void); -GtkAdjustment *sysprof_zoom_manager_get_adjustment (SysprofZoomManager *self); -gboolean sysprof_zoom_manager_get_can_zoom_in (SysprofZoomManager *self); -gboolean sysprof_zoom_manager_get_can_zoom_out (SysprofZoomManager *self); -gboolean sysprof_zoom_manager_get_min_zoom (SysprofZoomManager *self); -gboolean sysprof_zoom_manager_get_max_zoom (SysprofZoomManager *self); -void sysprof_zoom_manager_set_min_zoom (SysprofZoomManager *self, - gdouble min_zoom); -void sysprof_zoom_manager_set_max_zoom (SysprofZoomManager *self, - gdouble max_zoom); -void sysprof_zoom_manager_zoom_in (SysprofZoomManager *self); -void sysprof_zoom_manager_zoom_out (SysprofZoomManager *self); -void sysprof_zoom_manager_reset (SysprofZoomManager *self); -gdouble sysprof_zoom_manager_get_zoom (SysprofZoomManager *self); -void sysprof_zoom_manager_set_zoom (SysprofZoomManager *self, - gdouble zoom); -gchar *sysprof_zoom_manager_get_zoom_label (SysprofZoomManager *self); -gint sysprof_zoom_manager_get_width_for_duration (SysprofZoomManager *self, - gint64 duration); -gint64 sysprof_zoom_manager_get_duration_for_width (SysprofZoomManager *self, - gint width); -gdouble sysprof_zoom_manager_fit_zoom_for_duration (SysprofZoomManager *self, - gint64 duration, - gint width); -gdouble sysprof_zoom_manager_get_offset_at_time (SysprofZoomManager *self, - gint64 offset, - gint width); - -G_END_DECLS diff --git a/src/meson.build b/src/meson.build index a2a841c5..f19e2ddc 100644 --- a/src/meson.build +++ b/src/meson.build @@ -74,7 +74,6 @@ if get_option('libsysprof') or get_option('agent') subdir('libsysprof') endif if get_option('gtk') and get_option('libsysprof') - subdir('libsysprof-ui') subdir('libsysprof-gtk') endif if get_option('gtk') and get_option('libsysprof') diff --git a/src/tests/meson.build b/src/tests/meson.build index 611ff260..689bd110 100644 --- a/src/tests/meson.build +++ b/src/tests/meson.build @@ -112,37 +112,4 @@ if get_option('libsysprof') dependencies: [libsysprof_static_dep], include_directories: include_directories('..'), ) - - if get_option('gtk') - test_ui_deps = [ - libsysprof_dep, - libsysprof_ui_dep, - dependency('gtk4', version: gtk_req_version), - dependency('pangoft2', required: false), - ] - - test_model_filter = executable('test-model-filter', 'test-model-filter.c', - c_args: test_cflags, - dependencies: test_ui_deps, - ) - - test_process_model = executable('test-process-model', 'test-process-model.c', - c_args: test_cflags, - dependencies: test_ui_deps, - ) - - test_zoom = executable('test-zoom', - ['test-zoom.c', '../libsysprof-ui/sysprof-zoom-manager.c'], - c_args: test_cflags, - dependencies: test_ui_deps, - ) - - test_capture_view = executable('test-capture-view', 'test-capture-view.c', - c_args: test_cflags, - dependencies: test_ui_deps, - ) - - test('test-model-filter', test_model_filter, env: test_env) - test('test-zoom', test_zoom, env: test_env) - endif endif From 88d0589fea80307fe8d49a9c4f93ef99cb6a5e8b Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 12 Jul 2023 10:26:50 -0700 Subject: [PATCH 0824/1030] libsysprof-profile: tail journald and append logs to capture It can be handy to get system information from journald to correlate with what is going on in applications. This simple journald tail GSource will dispatch to our callback which can append the logs to the capture. This uses a custom callback rather than the GSourceFunc because that seems a bit annoying to use with recent GCC function equivalence checks. --- config.h.meson | 2 + meson.build | 2 + src/libsysprof-profile/meson.build | 4 + .../sysprof-journald-source.c | 107 ++++++++++ .../sysprof-journald-source.h | 36 ++++ src/libsysprof-profile/sysprof-profile.h | 1 + src/libsysprof-profile/sysprof-system-logs.c | 194 ++++++++++++++++++ src/libsysprof-profile/sysprof-system-logs.h | 43 ++++ src/libsysprof-profile/tests/test-profiler.c | 1 + src/sysprof/sysprof-greeter.c | 5 + src/sysprof/sysprof-greeter.ui | 18 ++ 11 files changed, 413 insertions(+) create mode 100644 src/libsysprof-profile/sysprof-journald-source.c create mode 100644 src/libsysprof-profile/sysprof-journald-source.h create mode 100644 src/libsysprof-profile/sysprof-system-logs.c create mode 100644 src/libsysprof-profile/sysprof-system-logs.h diff --git a/config.h.meson b/config.h.meson index 34be5f6a..b002f492 100644 --- a/config.h.meson +++ b/config.h.meson @@ -16,6 +16,8 @@ #mesondefine HAVE_EXECINFO_H +#mesondefine HAVE_LIBSYSTEMD + #mesondefine HAVE_PERF_CLOCKID #mesondefine HAVE_POLKIT diff --git a/meson.build b/meson.build index 3d330d58..373d5f8c 100644 --- a/meson.build +++ b/meson.build @@ -61,6 +61,7 @@ cxx = meson.get_compiler('cpp') glib_dep = dependency('glib-2.0', version: glib_req_version, required: need_glib) gtk_dep = dependency('gtk4', version: gtk_req_version, required: need_gtk) +libsystemd_dep = dependency('libsystemd', required: false) config_h = configuration_data() config_h.set_quoted('SYMBOLIC_VERSION', symbolic_version) @@ -104,6 +105,7 @@ config_h.set('LOCALEDIR', 'PACKAGE_LOCALE_DIR') 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()) polkit_agent_dep = dependency('polkit-agent-1', required: false) config_h.set10('HAVE_POLKIT_AGENT', polkit_agent_dep.found()) diff --git a/src/libsysprof-profile/meson.build b/src/libsysprof-profile/meson.build index 5f17ff0c..24488a98 100644 --- a/src/libsysprof-profile/meson.build +++ b/src/libsysprof-profile/meson.build @@ -14,6 +14,7 @@ libsysprof_profile_public_sources = [ 'sysprof-recording.c', 'sysprof-sampler.c', 'sysprof-spawnable.c', + 'sysprof-system-logs.c', 'sysprof-tracer.c', ] @@ -22,6 +23,7 @@ libsysprof_profile_private_sources = [ 'sysprof-controlfd-instrument.c', 'sysprof-maps-parser.c', 'sysprof-perf-event-stream.c', + 'sysprof-journald-source.c', 'sysprof-podman.c', 'sysprof-polkit.c', ] @@ -44,6 +46,7 @@ libsysprof_profile_public_headers = [ 'sysprof-recording.h', 'sysprof-sampler.h', 'sysprof-spawnable.h', + 'sysprof-system-logs.h', 'sysprof-tracer.h', ] @@ -61,6 +64,7 @@ libsysprof_profile_deps = [ dependency('json-glib-1.0'), dependency('libdex-1', version: dex_req_version), dependency('polkit-gobject-1', version: polkit_req_version), + libsystemd_dep, liblinereader_static_dep, libsysprof_capture_dep, diff --git a/src/libsysprof-profile/sysprof-journald-source.c b/src/libsysprof-profile/sysprof-journald-source.c new file mode 100644 index 00000000..fad3199b --- /dev/null +++ b/src/libsysprof-profile/sysprof-journald-source.c @@ -0,0 +1,107 @@ +/* sysprof-journald-source.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include + +#include "sysprof-journald-source.h" + +typedef struct _SysprofJournaldSource +{ + GSource parent_instance; + sd_journal *journal; + SysprofJournaldSourceFunc func; + gpointer func_data; + GDestroyNotify func_data_destroy; +} SysprofJournaldSource; + +static void +sysprof_journald_source_finalize (GSource *source) +{ + SysprofJournaldSource *self = (SysprofJournaldSource *)source; + + if (self->func_data_destroy) + self->func_data_destroy (self->func_data); + + g_clear_pointer (&self->journal, sd_journal_close); +} + +static gboolean +sysprof_journald_source_dispatch (GSource *source, + GSourceFunc callback, + gpointer user_data) +{ + SysprofJournaldSource *self = (SysprofJournaldSource *)source; + + if (sd_journal_process (self->journal) == SD_JOURNAL_APPEND) + { + if (sd_journal_next (self->journal) > 0) + return self->func (self->func_data, self->journal); + } + + return G_SOURCE_CONTINUE; +} + +static GSourceFuncs sysprof_journald_source_funcs = { + .dispatch = sysprof_journald_source_dispatch, + .finalize = sysprof_journald_source_finalize, +}; + +GSource * +sysprof_journald_source_new (SysprofJournaldSourceFunc func, + gpointer func_data, + GDestroyNotify func_data_destroy) +{ + SysprofJournaldSource *self; + sd_journal *journal = NULL; + int fd; + + if (sd_journal_open (&journal, 0) < 0) + return NULL; + + if (sd_journal_seek_tail (journal) < 0) + goto failure; + + if (sd_journal_previous (journal) < 0) + goto failure; + + if (sd_journal_next (journal) < 0) + goto failure; + + if (-1 == (fd = sd_journal_get_fd (journal))) + goto failure; + + self = (SysprofJournaldSource *)g_source_new (&sysprof_journald_source_funcs, + sizeof (SysprofJournaldSource)); + self->journal = journal; + self->func = func; + self->func_data = func_data; + self->func_data_destroy = func_data_destroy; + + g_source_add_unix_fd ((GSource *)self, fd, sd_journal_get_events (journal)); + + return (GSource *)self; + +failure: + g_clear_pointer (&journal, sd_journal_close); + + return NULL; +} diff --git a/src/libsysprof-profile/sysprof-journald-source.h b/src/libsysprof-profile/sysprof-journald-source.h new file mode 100644 index 00000000..5e4552e9 --- /dev/null +++ b/src/libsysprof-profile/sysprof-journald-source.h @@ -0,0 +1,36 @@ +/* sysprof-journald-source.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +#include + +G_BEGIN_DECLS + +typedef gboolean (*SysprofJournaldSourceFunc) (gpointer user_data, + sd_journal *journal); + +GSource *sysprof_journald_source_new (SysprofJournaldSourceFunc func, + gpointer func_data, + GDestroyNotify func_data_destroy); + +G_END_DECLS diff --git a/src/libsysprof-profile/sysprof-profile.h b/src/libsysprof-profile/sysprof-profile.h index 6640985a..6b312319 100644 --- a/src/libsysprof-profile/sysprof-profile.h +++ b/src/libsysprof-profile/sysprof-profile.h @@ -40,6 +40,7 @@ G_BEGIN_DECLS # include "sysprof-recording.h" # include "sysprof-sampler.h" # include "sysprof-spawnable.h" +# include "sysprof-system-logs.h" # include "sysprof-tracer.h" #undef SYSPROF_PROFILE_INSIDE diff --git a/src/libsysprof-profile/sysprof-system-logs.c b/src/libsysprof-profile/sysprof-system-logs.c new file mode 100644 index 00000000..6652aba7 --- /dev/null +++ b/src/libsysprof-profile/sysprof-system-logs.c @@ -0,0 +1,194 @@ +/* sysprof-system-logs.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include + +#include +#include + +#include "sysprof-system-logs.h" +#include "sysprof-instrument-private.h" +#include "sysprof-recording-private.h" + +#ifdef HAVE_LIBSYSTEMD +# include "sysprof-journald-source.h" +#endif + +struct _SysprofSystemLogs +{ + SysprofInstrument parent_instance; +}; + +struct _SysprofSystemLogsClass +{ + SysprofInstrumentClass parent_class; +}; + +G_DEFINE_FINAL_TYPE (SysprofSystemLogs, sysprof_system_logs, SYSPROF_TYPE_INSTRUMENT) + +#ifdef HAVE_LIBSYSTEMD +static char * +journal_get_data (sd_journal *journal, + const char *field) +{ + gconstpointer data = NULL; + const char *endptr; + const char *eq; + gsize length; + int ret; + + g_assert (journal != NULL); + g_assert (field != NULL); + + if ((ret = sd_journal_get_data (journal, field, &data, &length)) < 0) + return NULL; + + endptr = (const char *)data + length; + if (!(eq = memchr (data, '=', length))) + return NULL; + + eq++; + + return g_strndup (eq, endptr - eq); +} + +static gboolean +sysprof_system_logs_callback (SysprofCaptureWriter *writer, + sd_journal *journal) +{ + g_autofree char *message = NULL; + g_autofree char *priority = NULL; + sd_id128_t boot_id; + guint64 usec; + int severity = 0; + + g_assert (journal != NULL); + g_assert (writer != NULL); + + if (sd_journal_get_monotonic_usec (journal, &usec, &boot_id) != 0 || + !(message = journal_get_data (journal, "MESSAGE"))) + return G_SOURCE_CONTINUE; + + priority = journal_get_data (journal, "PRIORITY"); + + if (priority != NULL) + { + switch (priority[0]) + { + case '0': + case '1': + severity = G_LOG_LEVEL_ERROR; + break; + + case '2': + severity = G_LOG_LEVEL_CRITICAL; + break; + + case '3': + case '4': + severity = G_LOG_LEVEL_WARNING; + break; + + default: + case '5': + severity = G_LOG_LEVEL_MESSAGE; + break; + + case '6': + severity = G_LOG_LEVEL_INFO; + break; + + case '7': + severity = G_LOG_LEVEL_DEBUG; + break; + } + } + + sysprof_capture_writer_add_log (writer, usec*1000, -1, -1, severity, "System Log", message); + + return G_SOURCE_CONTINUE; +} +#endif + +#ifdef HAVE_LIBSYSTEMD +static DexFuture * +sysprof_system_logs_record_finished (DexFuture *future, + gpointer user_data) +{ + GSource *source = user_data; + g_source_destroy (source); + return dex_future_new_for_boolean (TRUE); +} +#endif + +static DexFuture * +sysprof_system_logs_record (SysprofInstrument *instrument, + SysprofRecording *recording, + GCancellable *cancellable) +{ +#ifdef HAVE_LIBSYSTEMD + SysprofCaptureWriter *writer; + GSource *source; + + g_assert (SYSPROF_IS_SYSTEM_LOGS (instrument)); + g_assert (SYSPROF_IS_RECORDING (recording)); + g_assert (G_IS_CANCELLABLE (cancellable)); + + writer = _sysprof_recording_writer (recording); + + source = sysprof_journald_source_new ((SysprofJournaldSourceFunc)sysprof_system_logs_callback, + sysprof_capture_writer_ref (writer), + (GDestroyNotify)sysprof_capture_writer_unref); + g_source_set_static_name (source, "[sysprof-journald]"); + g_source_set_priority (source, G_PRIORITY_LOW); + g_source_attach (source, NULL); + + return dex_future_finally (dex_cancellable_new_from_cancellable (cancellable), + sysprof_system_logs_record_finished, + source, + (GDestroyNotify)g_source_unref); +#else + _sysprof_recording_diagnostic (recording, + _("System Logs"), + _("Recording system logs is not supported on your platform.")); + return dex_future_new_for_boolean (TRUE); +#endif +} + +static void +sysprof_system_logs_class_init (SysprofSystemLogsClass *klass) +{ + SysprofInstrumentClass *instrument_class = SYSPROF_INSTRUMENT_CLASS (klass); + + instrument_class->record = sysprof_system_logs_record; +} + +static void +sysprof_system_logs_init (SysprofSystemLogs *self) +{ +} + +SysprofInstrument * +sysprof_system_logs_new (void) +{ + return g_object_new (SYSPROF_TYPE_SYSTEM_LOGS, NULL); +} diff --git a/src/libsysprof-profile/sysprof-system-logs.h b/src/libsysprof-profile/sysprof-system-logs.h new file mode 100644 index 00000000..1739f722 --- /dev/null +++ b/src/libsysprof-profile/sysprof-system-logs.h @@ -0,0 +1,43 @@ +/* sysprof-system-logs.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-instrument.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_SYSTEM_LOGS (sysprof_system_logs_get_type()) +#define SYSPROF_IS_SYSTEM_LOGS(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_SYSTEM_LOGS) +#define SYSPROF_SYSTEM_LOGS(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_SYSTEM_LOGS, SysprofSystemLogs) +#define SYSPROF_SYSTEM_LOGS_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_SYSTEM_LOGS, SysprofSystemLogsClass) + +typedef struct _SysprofSystemLogs SysprofSystemLogs; +typedef struct _SysprofSystemLogsClass SysprofSystemLogsClass; + +SYSPROF_AVAILABLE_IN_ALL +GType sysprof_system_logs_get_type (void) G_GNUC_CONST; +SYSPROF_AVAILABLE_IN_ALL +SysprofInstrument *sysprof_system_logs_new (void); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofSystemLogs, g_object_unref) + +G_END_DECLS + diff --git a/src/libsysprof-profile/tests/test-profiler.c b/src/libsysprof-profile/tests/test-profiler.c index 1aa3264d..af88276a 100644 --- a/src/libsysprof-profile/tests/test-profiler.c +++ b/src/libsysprof-profile/tests/test-profiler.c @@ -178,6 +178,7 @@ main (int argc, sysprof_profiler_add_instrument (profiler, sysprof_energy_usage_new ()); sysprof_profiler_add_instrument (profiler, sysprof_memory_usage_new ()); sysprof_profiler_add_instrument (profiler, sysprof_network_usage_new ()); + sysprof_profiler_add_instrument (profiler, sysprof_system_logs_new ()); if (power_profile) sysprof_profiler_add_instrument (profiler, sysprof_power_profile_new (power_profile)); diff --git a/src/sysprof/sysprof-greeter.c b/src/sysprof/sysprof-greeter.c index c7176adf..81d596ba 100644 --- a/src/sysprof/sysprof-greeter.c +++ b/src/sysprof/sysprof-greeter.c @@ -44,6 +44,7 @@ struct _SysprofGreeter GtkSwitch *record_disk_usage; GtkSwitch *record_network_usage; GtkSwitch *record_compositor; + GtkSwitch *record_system_logs; }; enum { @@ -91,6 +92,9 @@ sysprof_greeter_create_profiler (SysprofGreeter *self) "org.gnome.Shell", "/org/gnome/Sysprof3/Profiler")); + if (gtk_switch_get_active (self->record_system_logs)) + sysprof_profiler_add_instrument (profiler, sysprof_system_logs_new ()); + return g_steal_pointer (&profiler); } @@ -282,6 +286,7 @@ sysprof_greeter_class_init (SysprofGreeterClass *klass) gtk_widget_class_bind_template_child (widget_class, SysprofGreeter, record_disk_usage); gtk_widget_class_bind_template_child (widget_class, SysprofGreeter, record_network_usage); gtk_widget_class_bind_template_child (widget_class, SysprofGreeter, record_page); + gtk_widget_class_bind_template_child (widget_class, SysprofGreeter, record_system_logs); gtk_widget_class_bind_template_child (widget_class, SysprofGreeter, sample_native_stacks); gtk_widget_class_bind_template_child (widget_class, SysprofGreeter, toolbar); gtk_widget_class_bind_template_child (widget_class, SysprofGreeter, view_stack); diff --git a/src/sysprof/sysprof-greeter.ui b/src/sysprof/sysprof-greeter.ui index 2e9bbc94..796c9e5d 100644 --- a/src/sysprof/sysprof-greeter.ui +++ b/src/sysprof/sysprof-greeter.ui @@ -223,6 +223,24 @@ + + + System + + + record_system_logs + System Logs + Watch the system log for new messages and record them + + + true + center + + + + + + From 8c85cca0625727e43865ab1fc23e2c2d863f2c4b Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 12 Jul 2023 10:37:56 -0700 Subject: [PATCH 0825/1030] libsysprof-gtk: add some spacing for section header --- src/libsysprof-gtk/sysprof-mark-chart.ui | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libsysprof-gtk/sysprof-mark-chart.ui b/src/libsysprof-gtk/sysprof-mark-chart.ui index 1faede9c..02e3c200 100644 --- a/src/libsysprof-gtk/sysprof-mark-chart.ui +++ b/src/libsysprof-gtk/sysprof-mark-chart.ui @@ -22,6 +22,7 @@ 0 + 6 From 78a17d5d528fea4863adb62a8207fe53e2092f1c Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 12 Jul 2023 11:15:50 -0700 Subject: [PATCH 0826/1030] libsysprof-analyze: add API to get CPU info as objects This can be handy to use from UI so we can bind it to UI elements. --- src/libsysprof-analyze/meson.build | 4 +- src/libsysprof-analyze/sysprof-analyze.h | 1 + .../sysprof-cpu-info-private.h | 30 ++++ src/libsysprof-analyze/sysprof-cpu-info.c | 150 ++++++++++++++++++ src/libsysprof-analyze/sysprof-cpu-info.h | 39 +++++ src/libsysprof-analyze/sysprof-document.c | 103 ++++++++++++ src/libsysprof-analyze/sysprof-document.h | 2 + src/libsysprof-analyze/tests/meson.build | 1 + src/libsysprof-analyze/tests/test-list-cpu.c | 76 +++++++++ 9 files changed, 405 insertions(+), 1 deletion(-) create mode 100644 src/libsysprof-analyze/sysprof-cpu-info-private.h create mode 100644 src/libsysprof-analyze/sysprof-cpu-info.c create mode 100644 src/libsysprof-analyze/sysprof-cpu-info.h create mode 100644 src/libsysprof-analyze/tests/test-list-cpu.c diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build index 67536961..c039dae0 100644 --- a/src/libsysprof-analyze/meson.build +++ b/src/libsysprof-analyze/meson.build @@ -3,6 +3,7 @@ libsysprof_analyze_public_sources = [ 'sysprof-callgraph.c', 'sysprof-callgraph-frame.c', 'sysprof-callgraph-symbol.c', + 'sysprof-cpu-info.c', 'sysprof-document.c', 'sysprof-document-allocation.c', 'sysprof-document-counter.c', @@ -37,10 +38,11 @@ libsysprof_analyze_public_sources = [ libsysprof_analyze_public_headers = [ 'sysprof-analyze.h', + 'sysprof-bundled-symbolizer.h', 'sysprof-callgraph.h', 'sysprof-callgraph-frame.h', 'sysprof-callgraph-symbol.h', - 'sysprof-bundled-symbolizer.h', + 'sysprof-cpu-info.h', 'sysprof-document.h', 'sysprof-document-allocation.h', 'sysprof-document-counter.h', diff --git a/src/libsysprof-analyze/sysprof-analyze.h b/src/libsysprof-analyze/sysprof-analyze.h index b830b281..ab4d9ca2 100644 --- a/src/libsysprof-analyze/sysprof-analyze.h +++ b/src/libsysprof-analyze/sysprof-analyze.h @@ -29,6 +29,7 @@ G_BEGIN_DECLS # include "sysprof-callgraph.h" # include "sysprof-callgraph-frame.h" # include "sysprof-callgraph-symbol.h" +# include "sysprof-cpu-info.h" # include "sysprof-document.h" # include "sysprof-document-allocation.h" # include "sysprof-document-counter.h" diff --git a/src/libsysprof-analyze/sysprof-cpu-info-private.h b/src/libsysprof-analyze/sysprof-cpu-info-private.h new file mode 100644 index 00000000..ef785717 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-cpu-info-private.h @@ -0,0 +1,30 @@ +/* sysprof-cpu-info-private.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-cpu-info.h" + +G_BEGIN_DECLS + +void _sysprof_cpu_info_set_model_name (SysprofCpuInfo *self, + const char *model_name); + +G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-cpu-info.c b/src/libsysprof-analyze/sysprof-cpu-info.c new file mode 100644 index 00000000..b24b43b2 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-cpu-info.c @@ -0,0 +1,150 @@ +/* sysprof-cpu-info.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-cpu-info.h" + +struct _SysprofCpuInfo +{ + GObject parent_instance; + char *model_name; + guint id; +}; + +enum { + PROP_0, + PROP_ID, + PROP_MODEL_NAME, + N_PROPS +}; + +G_DEFINE_FINAL_TYPE (SysprofCpuInfo, sysprof_cpu_info, G_TYPE_OBJECT) + +static GParamSpec *properties [N_PROPS]; + +static void +sysprof_cpu_info_finalize (GObject *object) +{ + SysprofCpuInfo *self = (SysprofCpuInfo *)object; + + g_clear_pointer (&self->model_name, g_free); + + G_OBJECT_CLASS (sysprof_cpu_info_parent_class)->finalize (object); +} + +static void +sysprof_cpu_info_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofCpuInfo *self = SYSPROF_CPU_INFO (object); + + switch (prop_id) + { + case PROP_ID: + g_value_set_uint (value, self->id); + break; + + case PROP_MODEL_NAME: + g_value_set_string (value, self->model_name); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_cpu_info_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SysprofCpuInfo *self = SYSPROF_CPU_INFO (object); + + switch (prop_id) + { + case PROP_ID: + self->id = g_value_get_uint (value); + break; + + case PROP_MODEL_NAME: + self->model_name = g_value_dup_string (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_cpu_info_class_init (SysprofCpuInfoClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = sysprof_cpu_info_finalize; + object_class->get_property = sysprof_cpu_info_get_property; + object_class->set_property = sysprof_cpu_info_set_property; + + properties[PROP_ID] = + g_param_spec_uint ("id", NULL, NULL, + 0, G_MAXUINT, 0, + (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + + properties[PROP_MODEL_NAME] = + g_param_spec_string ("model-name", NULL, NULL, + NULL, + (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); +} + +static void +sysprof_cpu_info_init (SysprofCpuInfo *self) +{ +} + +const char * +sysprof_cpu_info_get_model_name (SysprofCpuInfo *self) +{ + g_return_val_if_fail (SYSPROF_IS_CPU_INFO (self), NULL); + + return self->model_name; +} + +guint +sysprof_cpu_info_get_id (SysprofCpuInfo *self) +{ + g_return_val_if_fail (SYSPROF_IS_CPU_INFO (self), 0); + + return self->id; +} + +void +_sysprof_cpu_info_set_model_name (SysprofCpuInfo *self, + const char *model_name) +{ + g_return_if_fail (SYSPROF_IS_CPU_INFO (self)); + + if (g_set_str (&self->model_name, model_name)) + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODEL_NAME]); +} diff --git a/src/libsysprof-analyze/sysprof-cpu-info.h b/src/libsysprof-analyze/sysprof-cpu-info.h new file mode 100644 index 00000000..b61babe4 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-cpu-info.h @@ -0,0 +1,39 @@ +/* sysprof-cpu-info.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +#include + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_CPU_INFO (sysprof_cpu_info_get_type()) + +SYSPROF_AVAILABLE_IN_ALL +G_DECLARE_FINAL_TYPE (SysprofCpuInfo, sysprof_cpu_info, SYSPROF, CPU_INFO, GObject) + +SYSPROF_AVAILABLE_IN_ALL +guint sysprof_cpu_info_get_id (SysprofCpuInfo *self); +SYSPROF_AVAILABLE_IN_ALL +const char *sysprof_cpu_info_get_model_name (SysprofCpuInfo *self); + +G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index a1b79d1e..5df46f5a 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -28,6 +28,7 @@ #include "sysprof-document-private.h" #include "sysprof-callgraph-private.h" +#include "sysprof-cpu-info-private.h" #include "sysprof-document-bitset-index-private.h" #include "sysprof-document-counter-private.h" #include "sysprof-document-ctrdef.h" @@ -65,6 +66,8 @@ struct _SysprofDocument GMappedFile *mapped_file; const guint8 *base; + GListStore *cpu_info; + GListStore *counters; GHashTable *counter_id_to_values; @@ -109,6 +112,7 @@ enum { PROP_0, PROP_ALLOCATIONS, PROP_COUNTERS, + PROP_CPU_INFO, PROP_FILES, PROP_LOGS, PROP_METADATA, @@ -337,6 +341,8 @@ sysprof_document_finalize (GObject *object) g_clear_object (&self->counters); g_clear_pointer (&self->counter_id_to_values, g_hash_table_unref); + g_clear_object (&self->cpu_info); + g_clear_object (&self->mount_namespace); g_clear_object (&self->symbols); @@ -363,6 +369,10 @@ sysprof_document_get_property (GObject *object, g_value_take_object (value, sysprof_document_list_counters (self)); break; + case PROP_CPU_INFO: + g_value_take_object (value, sysprof_document_list_cpu_info (self)); + break; + case PROP_FILES: g_value_take_object (value, sysprof_document_list_files (self)); break; @@ -414,6 +424,11 @@ sysprof_document_class_init (SysprofDocumentClass *klass) G_TYPE_LIST_MODEL, (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + properties [PROP_CPU_INFO] = + g_param_spec_object ("cpu-info", NULL, NULL, + G_TYPE_LIST_MODEL, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + properties [PROP_FILES] = g_param_spec_object ("files", NULL, NULL, G_TYPE_LIST_MODEL, @@ -459,6 +474,8 @@ sysprof_document_init (SysprofDocument *self) self->frames = g_array_new (FALSE, FALSE, sizeof (SysprofDocumentFramePointer)); + self->cpu_info = g_list_store_new (SYSPROF_TYPE_CPU_INFO); + self->counters = g_list_store_new (SYSPROF_TYPE_DOCUMENT_COUNTER); self->counter_id_to_values = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_array_unref); @@ -980,6 +997,74 @@ sysprof_document_update_process_exit_times (SysprofDocument *self) } } +static void +sysprof_document_load_cpu (SysprofDocument *self) +{ + const gsize model_len = strlen ("Model\t\t: "); + g_autoptr(SysprofDocumentFile) file = NULL; + g_autoptr(GBytes) bytes = NULL; + g_autoptr(SysprofCpuInfo) cpu_info = NULL; + g_autofree char *model = NULL; + const char *str; + const char *line; + LineReader reader; + gsize line_len; + gsize len; + + g_assert (SYSPROF_IS_DOCUMENT (self)); + + if (!(file = sysprof_document_lookup_file (self, "/proc/cpuinfo")) || + !(bytes = sysprof_document_file_dup_bytes (file))) + return; + + str = (const char *)g_bytes_get_data (bytes, &len); + + line_reader_init (&reader, (char *)str, len); + while ((line = line_reader_next (&reader, &line_len))) + { + if (g_str_has_prefix (line, "processor\t: ")) + { + gint64 id = g_ascii_strtoll (line+strlen("processor\t: "), NULL, 10); + + if (cpu_info != NULL) + g_list_store_append (self->cpu_info, cpu_info); + + g_clear_object (&cpu_info); + + cpu_info = g_object_new (SYSPROF_TYPE_CPU_INFO, + "id", id, + NULL); + } + + if (g_str_has_prefix (line, "model name\t: ")) + { + const gsize model_name_len = strlen ("model name\t: "); + g_autofree char *model_name = g_strndup (line+model_name_len, line_len-model_name_len); + + if (cpu_info != NULL) + _sysprof_cpu_info_set_model_name (cpu_info, model_name); + } + + if (!model && g_str_has_prefix (line, "Model\t\t: ")) + model = g_strndup (line+model_len, line_len-model_len); + } + + if (cpu_info != NULL) + g_list_store_append (self->cpu_info, cpu_info); + + if (model != NULL) + { + guint n_items = g_list_model_get_n_items (G_LIST_MODEL (self->cpu_info)); + + for (guint i = 0; i < n_items; i++) + { + g_autoptr(SysprofCpuInfo) item = g_list_model_get_item (G_LIST_MODEL (self->cpu_info), i); + + _sysprof_cpu_info_set_model_name (item, model); + } + } +} + static void sysprof_document_load_worker (GTask *task, gpointer source_object, @@ -1223,6 +1308,8 @@ sysprof_document_load_worker (GTask *task, if (guessed_end_nsec > self->time_span.begin_nsec) self->time_span.end_nsec = guessed_end_nsec; + sysprof_document_load_cpu (self); + load_progress (load, .6, _("Discovering file system mounts")); sysprof_document_load_mounts (self); @@ -2186,3 +2273,19 @@ _sysprof_document_set_title (SysprofDocument *self, if (g_set_str (&self->title, title)) g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_TITLE]); } + +/** + * sysprof_document_list_cpu_info: + * @self: a #SysprofDocument + * + * Gets the CPU that were discovered from the capture. + * + * Returns: (transfer full): a #GListModel of #SysprofCpuInfo + */ +GListModel * +sysprof_document_list_cpu_info (SysprofDocument *self) +{ + g_return_val_if_fail (SYSPROF_IS_DOCUMENT (self), NULL); + + return g_object_ref (G_LIST_MODEL (self->cpu_info)); +} diff --git a/src/libsysprof-analyze/sysprof-document.h b/src/libsysprof-analyze/sysprof-document.h index c800bcb9..baa53b09 100644 --- a/src/libsysprof-analyze/sysprof-document.h +++ b/src/libsysprof-analyze/sysprof-document.h @@ -47,6 +47,8 @@ SYSPROF_AVAILABLE_IN_ALL SysprofDocumentFile *sysprof_document_lookup_file (SysprofDocument *self, const char *path); SYSPROF_AVAILABLE_IN_ALL +GListModel *sysprof_document_list_cpu_info (SysprofDocument *self); +SYSPROF_AVAILABLE_IN_ALL GListModel *sysprof_document_list_files (SysprofDocument *self); SYSPROF_AVAILABLE_IN_ALL GListModel *sysprof_document_list_traceables (SysprofDocument *self); diff --git a/src/libsysprof-analyze/tests/meson.build b/src/libsysprof-analyze/tests/meson.build index 8f73e931..921b22b7 100644 --- a/src/libsysprof-analyze/tests/meson.build +++ b/src/libsysprof-analyze/tests/meson.build @@ -16,6 +16,7 @@ libsysprof_analyze_testsuite = { 'test-capture-model' : {'skip': true}, 'test-elf-loader' : {'skip': true}, 'test-list-counters' : {'skip': true}, + 'test-list-cpu' : {'skip': true}, 'test-list-files' : {'skip': true}, 'test-list-jitmap' : {'skip': true}, 'test-list-overlays' : {'skip': true}, diff --git a/src/libsysprof-analyze/tests/test-list-cpu.c b/src/libsysprof-analyze/tests/test-list-cpu.c new file mode 100644 index 00000000..23872b53 --- /dev/null +++ b/src/libsysprof-analyze/tests/test-list-cpu.c @@ -0,0 +1,76 @@ +/* test-list-counters.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include + +#include "sysprof-document-private.h" + +static const GOptionEntry entries[] = { + { 0 } +}; + +int +main (int argc, + char *argv[]) +{ + g_autoptr(GOptionContext) context = g_option_context_new ("- list cpu information from capture"); + g_autoptr(SysprofDocumentLoader) loader = NULL; + g_autoptr(SysprofDocument) document = NULL; + g_autoptr(GListModel) model = NULL; + g_autoptr(GError) error = NULL; + guint n_items; + + g_option_context_add_main_entries (context, entries, NULL); + + if (!g_option_context_parse (context, &argc, &argv, &error)) + { + g_printerr ("%s\n", error->message); + return 1; + } + + if (argc < 2) + { + g_printerr ("usage: %s CAPTURE_FILE\n", argv[0]); + return 1; + } + + loader = sysprof_document_loader_new (argv[1]); + sysprof_document_loader_set_symbolizer (loader, sysprof_no_symbolizer_get ()); + + if (!(document = sysprof_document_loader_load (loader, NULL, &error))) + { + g_printerr ("Failed to open capture: %s\n", error->message); + return 1; + } + + model = sysprof_document_list_cpu_info (document); + n_items = g_list_model_get_n_items (model); + + for (guint i = 0; i < n_items; i++) + { + g_autoptr(SysprofCpuInfo) cpu_info = g_list_model_get_item (model, i); + + g_print ("processor %u: %s\n", + sysprof_cpu_info_get_id (cpu_info), + sysprof_cpu_info_get_model_name (cpu_info)); + } + + return 0; +} From c4b5e3ec9929408dc6f62a7d0ae1b45887eb0d20 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 12 Jul 2023 11:33:42 -0700 Subject: [PATCH 0827/1030] sysprof: fix initial sorting of log entries --- src/sysprof/sysprof-logs-section.c | 10 ++++++++-- src/sysprof/sysprof-logs-section.ui | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/sysprof/sysprof-logs-section.c b/src/sysprof/sysprof-logs-section.c index cdd816be..63fae182 100644 --- a/src/sysprof/sysprof-logs-section.c +++ b/src/sysprof/sysprof-logs-section.c @@ -28,9 +28,10 @@ struct _SysprofLogsSection { - SysprofSection parent_instance; + SysprofSection parent_instance; - GtkColumnView *column_view; + GtkColumnView *column_view; + GtkColumnViewColumn *time_column; }; G_DEFINE_FINAL_TYPE (SysprofLogsSection, sysprof_logs_section, SYSPROF_TYPE_SECTION) @@ -80,6 +81,7 @@ sysprof_logs_section_class_init (SysprofLogsSectionClass *klass) gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/sysprof/sysprof-logs-section.ui"); gtk_widget_class_bind_template_child (widget_class, SysprofLogsSection, column_view); + gtk_widget_class_bind_template_child (widget_class, SysprofLogsSection, time_column); gtk_widget_class_bind_template_callback (widget_class, format_severity); g_type_ensure (SYSPROF_TYPE_DOCUMENT_LOG); @@ -90,4 +92,8 @@ static void sysprof_logs_section_init (SysprofLogsSection *self) { gtk_widget_init_template (GTK_WIDGET (self)); + + gtk_column_view_sort_by_column (self->column_view, + self->time_column, + GTK_SORT_ASCENDING); } diff --git a/src/sysprof/sysprof-logs-section.ui b/src/sysprof/sysprof-logs-section.ui index 8b36cd21..e78929a0 100644 --- a/src/sysprof/sysprof-logs-section.ui +++ b/src/sysprof/sysprof-logs-section.ui @@ -38,7 +38,7 @@ - + From 1f4f4d7e50ead2eef8fd7a93bbd1a583af5058dd Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 12 Jul 2023 11:49:55 -0700 Subject: [PATCH 0828/1030] build: keep libsysprof-capture version at 4 We haven't changed the format, so we really don't want to cause unnecessary churn by applications consuming this. --- meson.build | 8 ++++---- src/libsysprof-capture/meson.build | 4 ++-- src/meson.build | 1 - 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/meson.build b/meson.build index 373d5f8c..5aecd7a8 100644 --- a/meson.build +++ b/meson.build @@ -21,12 +21,12 @@ else endif # All libraries share an ABI revision as they are expected -# to be updated as a set. +# to be updated as a set. However, we keep libsysprof-capture +# at an older version since it's used as a static library +# from various platform tooling soname_major_version = 6 - -# Temporary until we update meson.build +libsysprof_capture_api_version = 4 libsysprof_api_version = soname_major_version -libsysprof_ui_api_version = soname_major_version version_split = meson.project_version().split('.') datadir = get_option('datadir') diff --git a/src/libsysprof-capture/meson.build b/src/libsysprof-capture/meson.build index cea56e62..fefa923a 100644 --- a/src/libsysprof-capture/meson.build +++ b/src/libsysprof-capture/meson.build @@ -49,7 +49,7 @@ libsysprof_capture_deps = [ ] libsysprof_capture = static_library( - 'sysprof-capture-@0@'.format(libsysprof_api_version), + 'sysprof-capture-@0@'.format(libsysprof_capture_api_version), (libsysprof_capture_sources + mapped_ring_buffer_sources), @@ -68,7 +68,7 @@ libsysprof_capture_dep = declare_dependency( dependencies: libsysprof_capture_deps, include_directories: libsysprof_capture_include_dirs, ) -meson.override_dependency('sysprof-capture-@0@'.format(libsysprof_api_version), libsysprof_capture_dep) +meson.override_dependency('sysprof-capture-@0@'.format(libsysprof_capture_api_version), libsysprof_capture_dep) if install_static pkgconfig.generate( diff --git a/src/meson.build b/src/meson.build index f19e2ddc..443bfc6b 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,5 +1,4 @@ sysprof_header_subdir = 'sysprof-@0@'.format(libsysprof_api_version) -sysprof_ui_header_subdir = 'sysprof-ui-@0@'.format(libsysprof_ui_api_version) sysprof_version_conf = configuration_data() sysprof_version = meson.project_version().split('.') From 1bcdb3aeac189512eb21d45549fc0c8387e86623 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 12 Jul 2023 13:14:09 -0700 Subject: [PATCH 0829/1030] libsysprof-profile: do not discard on cancel our subprocess wait --- src/libsysprof-profile/sysprof-recording.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libsysprof-profile/sysprof-recording.c b/src/libsysprof-profile/sysprof-recording.c index 656a9982..74dd2afa 100644 --- a/src/libsysprof-profile/sysprof-recording.c +++ b/src/libsysprof-profile/sysprof-recording.c @@ -95,13 +95,16 @@ _sysprof_recording_spawn (SysprofSpawnable *spawnable) { g_autoptr(GSubprocess) subprocess = NULL; g_autoptr(GError) error = NULL; + DexFuture *ret; g_assert (SYSPROF_IS_SPAWNABLE (spawnable)); if (!(subprocess = sysprof_spawnable_spawn (spawnable, &error))) return dex_future_new_for_error (g_steal_pointer (&error)); - return dex_subprocess_wait_check (subprocess); + ret = dex_subprocess_wait_check (subprocess); + dex_async_pair_set_cancel_on_discard (DEX_ASYNC_PAIR (ret), FALSE); + return ret; } static inline void From a82e8e2d2aec381a520032defd2654b92afcb66a Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 12 Jul 2023 13:14:29 -0700 Subject: [PATCH 0830/1030] libsysprof-profile: record log message for stop command --- src/libsysprof-profile/sysprof-recording.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libsysprof-profile/sysprof-recording.c b/src/libsysprof-profile/sysprof-recording.c index 74dd2afa..9712d5a8 100644 --- a/src/libsysprof-profile/sysprof-recording.c +++ b/src/libsysprof-profile/sysprof-recording.c @@ -313,6 +313,7 @@ sysprof_recording_fiber (gpointer user_data) switch (command) { case SYSPROF_RECORDING_COMMAND_STOP: + g_debug ("Recording received stop command"); goto stop_recording; default: From e440c130e84fe0afc87b7431022513979e256349 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 12 Jul 2023 13:15:08 -0700 Subject: [PATCH 0831/1030] libsysprof-profiler: record sampler shutdown reason --- src/libsysprof-profile/sysprof-sampler.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/libsysprof-profile/sysprof-sampler.c b/src/libsysprof-profile/sysprof-sampler.c index 4c2ac1e4..e8ecd1b7 100644 --- a/src/libsysprof-profile/sysprof-sampler.c +++ b/src/libsysprof-profile/sysprof-sampler.c @@ -374,6 +374,7 @@ static DexFuture * sysprof_sampler_record_fiber (gpointer user_data) { Record *record = user_data; + g_autoptr(GError) error = NULL; g_assert (record != NULL); g_assert (SYSPROF_IS_SAMPLER (record->sampler)); @@ -383,24 +384,28 @@ sysprof_sampler_record_fiber (gpointer user_data) for (guint i = 0; i < record->sampler->perf_event_streams->len; i++) { SysprofPerfEventStream *stream = g_ptr_array_index (record->sampler->perf_event_streams, i); - g_autoptr(GError) error = NULL; if (!sysprof_perf_event_stream_enable (stream, &error)) g_debug ("%s", error->message); - g_debug ("Sampler %d enabled", i); + else + g_debug ("Sampler %d enabled", i); + + g_clear_error (&error); } - dex_await (dex_ref (record->cancellable), NULL); + if (!dex_await (dex_ref (record->cancellable), &error)) + g_debug ("Sampler shutting down for reason: %s", error->message); for (guint i = 0; i < record->sampler->perf_event_streams->len; i++) { SysprofPerfEventStream *stream = g_ptr_array_index (record->sampler->perf_event_streams, i); - g_autoptr(GError) error = NULL; if (!sysprof_perf_event_stream_disable (stream, &error)) g_debug ("%s", error->message); else g_debug ("Sampler %d disabled", i); + + g_clear_error (&error); } return dex_future_new_for_boolean (TRUE); From dc0c19c21130c641c7c2dcf349be87da684d84ef Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 12 Jul 2023 13:15:20 -0700 Subject: [PATCH 0832/1030] libsysprof-profile: flush recording before exit --- src/libsysprof-profile/tests/test-profiler.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libsysprof-profile/tests/test-profiler.c b/src/libsysprof-profile/tests/test-profiler.c index af88276a..1ac31e10 100644 --- a/src/libsysprof-profile/tests/test-profiler.c +++ b/src/libsysprof-profile/tests/test-profiler.c @@ -226,6 +226,8 @@ main (int argc, sysprof_capture_writer_cat (writer, reader); } + sysprof_capture_writer_flush (writer); + g_clear_pointer (&reader, sysprof_capture_reader_unref); g_clear_pointer (&writer, sysprof_capture_writer_unref); From 0cccd844b0d207485cec942bcad4ff2a21467eb8 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 12 Jul 2023 14:34:48 -0700 Subject: [PATCH 0833/1030] sysprof: start on utility pane --- src/sysprof/meson.build | 1 + src/sysprof/sysprof-traceables-utility.c | 137 ++++++++++++++++++++++ src/sysprof/sysprof-traceables-utility.h | 31 +++++ src/sysprof/sysprof-traceables-utility.ui | 10 ++ src/sysprof/sysprof-window.c | 34 ++++++ src/sysprof/sysprof-window.ui | 20 +++- src/sysprof/sysprof.gresource.xml | 1 + 7 files changed, 232 insertions(+), 2 deletions(-) create mode 100644 src/sysprof/sysprof-traceables-utility.c create mode 100644 src/sysprof/sysprof-traceables-utility.h create mode 100644 src/sysprof/sysprof-traceables-utility.ui diff --git a/src/sysprof/meson.build b/src/sysprof/meson.build index 8fc49316..d971be62 100644 --- a/src/sysprof/meson.build +++ b/src/sysprof/meson.build @@ -14,6 +14,7 @@ sysprof_sources = [ 'sysprof-section.c', 'sysprof-sidebar.c', 'sysprof-single-model.c', + 'sysprof-traceables-utility.c', 'sysprof-window.c', ] diff --git a/src/sysprof/sysprof-traceables-utility.c b/src/sysprof/sysprof-traceables-utility.c new file mode 100644 index 00000000..a3cb5f6e --- /dev/null +++ b/src/sysprof/sysprof-traceables-utility.c @@ -0,0 +1,137 @@ +/* sysprof-traceables-utility.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include + +#include "sysprof-traceables-utility.h" + +struct _SysprofTraceablesUtility +{ + GtkWidget parent_instance; + SysprofSession *session; + GListModel *traceables; +}; + +enum { + PROP_0, + PROP_SESSION, + PROP_TRACEABLES, + N_PROPS +}; + +G_DEFINE_FINAL_TYPE (SysprofTraceablesUtility, sysprof_traceables_utility, GTK_TYPE_WIDGET) + +static GParamSpec *properties[N_PROPS]; + +static void +sysprof_traceables_utility_finalize (GObject *object) +{ + SysprofTraceablesUtility *self = (SysprofTraceablesUtility *)object; + GtkWidget *child; + + gtk_widget_dispose_template (GTK_WIDGET (self), SYSPROF_TYPE_TRACEABLES_UTILITY); + + while ((child = gtk_widget_get_first_child (GTK_WIDGET (object)))) + gtk_widget_unparent (child); + + g_clear_object (&self->session); + + G_OBJECT_CLASS (sysprof_traceables_utility_parent_class)->finalize (object); +} + +static void +sysprof_traceables_utility_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofTraceablesUtility *self = SYSPROF_TRACEABLES_UTILITY (object); + + switch (prop_id) + { + case PROP_SESSION: + g_value_set_object (value, self->session); + break; + + case PROP_TRACEABLES: + g_value_set_object (value, self->traceables); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_traceables_utility_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SysprofTraceablesUtility *self = SYSPROF_TRACEABLES_UTILITY (object); + + switch (prop_id) + { + case PROP_SESSION: + g_set_object (&self->session, g_value_get_object (value)); + break; + + case PROP_TRACEABLES: + g_set_object (&self->traceables, g_value_get_object (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_traceables_utility_class_init (SysprofTraceablesUtilityClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->finalize = sysprof_traceables_utility_finalize; + object_class->get_property = sysprof_traceables_utility_get_property; + object_class->set_property = sysprof_traceables_utility_set_property; + + properties[PROP_SESSION] = + g_param_spec_object ("session", NULL, NULL, + SYSPROF_TYPE_SESSION, + (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); + + properties[PROP_TRACEABLES] = + g_param_spec_object ("traceables", NULL, NULL, + G_TYPE_LIST_MODEL, + (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-traceables-utility.ui"); + gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT); +} + +static void +sysprof_traceables_utility_init (SysprofTraceablesUtility *self) +{ + gtk_widget_init_template (GTK_WIDGET (self)); +} diff --git a/src/sysprof/sysprof-traceables-utility.h b/src/sysprof/sysprof-traceables-utility.h new file mode 100644 index 00000000..f76063b7 --- /dev/null +++ b/src/sysprof/sysprof-traceables-utility.h @@ -0,0 +1,31 @@ +/* sysprof-traceables-utility.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_TRACEABLES_UTILITY (sysprof_traceables_utility_get_type()) + +G_DECLARE_FINAL_TYPE (SysprofTraceablesUtility, sysprof_traceables_utility, SYSPROF, TRACEABLES_UTILITY, GtkWidget) + +G_END_DECLS diff --git a/src/sysprof/sysprof-traceables-utility.ui b/src/sysprof/sysprof-traceables-utility.ui new file mode 100644 index 00000000..7aadec01 --- /dev/null +++ b/src/sysprof/sysprof-traceables-utility.ui @@ -0,0 +1,10 @@ + + + + diff --git a/src/sysprof/sysprof-window.c b/src/sysprof/sysprof-window.c index 5f54e8e1..f7313fa0 100644 --- a/src/sysprof/sysprof-window.c +++ b/src/sysprof/sysprof-window.c @@ -33,6 +33,7 @@ #include "sysprof-processes-section.h" #include "sysprof-samples-section.h" #include "sysprof-sidebar.h" +#include "sysprof-traceables-utility.h" #include "sysprof-window.h" struct _SysprofWindow @@ -41,6 +42,9 @@ struct _SysprofWindow SysprofDocument *document; SysprofSession *session; + + AdwViewStack *utility_stack; + AdwWindowTitle *utility_title; }; enum { @@ -193,6 +197,9 @@ sysprof_window_class_init (SysprofWindowClass *klass) gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/sysprof/sysprof-window.ui"); + gtk_widget_class_bind_template_child (widget_class, SysprofWindow, utility_stack); + gtk_widget_class_bind_template_child (widget_class, SysprofWindow, utility_title); + gtk_widget_class_install_action (widget_class, "win.open-capture", NULL, sysprof_window_open_capture_action); gtk_widget_class_install_action (widget_class, "win.record-capture", NULL, sysprof_window_record_capture_action); @@ -206,12 +213,39 @@ sysprof_window_class_init (SysprofWindowClass *klass) g_type_ensure (SYSPROF_TYPE_SAMPLES_SECTION); g_type_ensure (SYSPROF_TYPE_SESSION); g_type_ensure (SYSPROF_TYPE_SIDEBAR); + g_type_ensure (SYSPROF_TYPE_TRACEABLES_UTILITY); +} + +static void +utility_stack_notify_visible_child_cb (SysprofWindow *self, + GParamSpec *pspec, + AdwViewStack *utility_stack) +{ + AdwViewStackPage *page; + const char *title = NULL; + GtkWidget *child; + + g_assert (SYSPROF_IS_WINDOW (self)); + g_assert (ADW_IS_VIEW_STACK (utility_stack)); + + if ((child = adw_view_stack_get_visible_child (utility_stack)) && + (page = adw_view_stack_get_page (utility_stack, child))) + title = adw_view_stack_page_get_title (page); + + adw_window_title_set_title (self->utility_title, title); } static void sysprof_window_init (SysprofWindow *self) { gtk_widget_init_template (GTK_WIDGET (self)); + + g_signal_connect_object (self->utility_stack, + "notify::visible-child", + G_CALLBACK (utility_stack_notify_visible_child_cb), + self, + G_CONNECT_SWAPPED); + utility_stack_notify_visible_child_cb (self, NULL, self->utility_stack); } GtkWidget * diff --git a/src/sysprof/sysprof-window.ui b/src/sysprof/sysprof-window.ui index 4ff12701..ae6d3251 100644 --- a/src/sysprof/sysprof-window.ui +++ b/src/sysprof/sysprof-window.ui @@ -169,11 +169,27 @@ - - + + + + true + + + Stack Traces + + + + SysprofWindow + + + + + + + diff --git a/src/sysprof/sysprof.gresource.xml b/src/sysprof/sysprof.gresource.xml index 659d79bc..0e49f338 100644 --- a/src/sysprof/sysprof.gresource.xml +++ b/src/sysprof/sysprof.gresource.xml @@ -14,6 +14,7 @@ sysprof-recording-pad.ui sysprof-samples-section.ui sysprof-sidebar.ui + sysprof-traceables-utility.ui sysprof-window.ui icons/scalable/actions/address-layout-symbolic.svg icons/scalable/actions/mark-chart-symbolic.svg From a234cc219748f4788d806917b6d1ffdd7350fbc8 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 12 Jul 2023 14:49:32 -0700 Subject: [PATCH 0834/1030] libsysprof-gtk: add SysprofCallgraphView:utillity-traceables This property is meant to hold accessory traceables that match the current selection in the callgraph view. Eventually we want this bridged with the callgraph sections to show something useful. --- .../sysprof-callgraph-view-private.h | 1 + src/libsysprof-gtk/sysprof-callgraph-view.c | 26 ++++++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/libsysprof-gtk/sysprof-callgraph-view-private.h b/src/libsysprof-gtk/sysprof-callgraph-view-private.h index 721f0aed..43df9c99 100644 --- a/src/libsysprof-gtk/sysprof-callgraph-view-private.h +++ b/src/libsysprof-gtk/sysprof-callgraph-view-private.h @@ -35,6 +35,7 @@ struct _SysprofCallgraphView SysprofDocument *document; SysprofCallgraph *callgraph; GListModel *traceables; + GListModel *utility_traceables; GtkColumnView *callers_column_view; GtkColumnView *descendants_column_view; diff --git a/src/libsysprof-gtk/sysprof-callgraph-view.c b/src/libsysprof-gtk/sysprof-callgraph-view.c index bca4ee3e..839d9d0b 100644 --- a/src/libsysprof-gtk/sysprof-callgraph-view.c +++ b/src/libsysprof-gtk/sysprof-callgraph-view.c @@ -34,6 +34,7 @@ enum { PROP_HIDE_SYSTEM_LIBRARIES, PROP_INCLUDE_THREADS, PROP_TRACEABLES, + PROP_UTILITY_TRACEABLES, N_PROPS }; @@ -50,6 +51,17 @@ G_DEFINE_ABSTRACT_TYPE_WITH_CODE (SysprofCallgraphView, sysprof_callgraph_view, static GParamSpec *properties [N_PROPS]; +static void +sysprof_callgraph_view_set_utility_traceables (SysprofCallgraphView *self, + GListModel *model) +{ + g_assert (SYSPROF_IS_CALLGRAPH_VIEW (self)); + g_assert (!model || G_IS_LIST_MODEL (model)); + + if (g_set_object (&self->utility_traceables, model)) + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_UTILITY_TRACEABLES]); +} + static void sysprof_callgraph_view_set_descendants (SysprofCallgraphView *self, GListModel *model) @@ -181,6 +193,8 @@ sysprof_callgraph_view_list_traceables_cb (GObject *object, traceables_selection_changed_cb (self, 0, 0, single); } + sysprof_callgraph_view_set_utility_traceables (self, model); + gtk_widget_set_visible (gtk_widget_get_parent (GTK_WIDGET (self->right_paned)), model && g_list_model_get_n_items (model)); } @@ -197,7 +211,7 @@ descendants_selection_changed_cb (SysprofCallgraphView *self, g_assert (SYSPROF_IS_CALLGRAPH_VIEW (self)); g_assert (GTK_IS_SINGLE_SELECTION (single)); - gtk_column_view_set_model (self->traceables_column_view, NULL); + sysprof_callgraph_view_set_utility_traceables (self, NULL); if ((object = gtk_single_selection_get_selected_item (single)) && GTK_IS_TREE_LIST_ROW (object) && @@ -364,6 +378,7 @@ sysprof_callgraph_view_dispose (GObject *object) g_clear_object (&self->callgraph); g_clear_object (&self->document); g_clear_object (&self->traceables); + g_clear_object (&self->utility_traceables); G_OBJECT_CLASS (sysprof_callgraph_view_parent_class)->dispose (object); } @@ -398,6 +413,10 @@ sysprof_callgraph_view_get_property (GObject *object, g_value_set_object (value, sysprof_callgraph_view_get_traceables (self)); break; + case PROP_UTILITY_TRACEABLES: + g_value_set_object (value, self->utility_traceables); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } @@ -469,6 +488,11 @@ sysprof_callgraph_view_class_init (SysprofCallgraphViewClass *klass) G_TYPE_LIST_MODEL, (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + properties[PROP_UTILITY_TRACEABLES] = + g_param_spec_object ("utility-traceables", NULL, NULL, + G_TYPE_LIST_MODEL, + (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_properties (object_class, N_PROPS, properties); gtk_widget_class_set_template_from_resource (widget_class, "/libsysprof-gtk/sysprof-callgraph-view.ui"); From 27f429d4f2d49d724be73b32d5892b43362078c2 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 12 Jul 2023 15:12:04 -0700 Subject: [PATCH 0835/1030] libsysprof-gtk: use models and databinding for traceables This is just working towards less code and more bindings for various parts of the traceables display (so we can move it out of this view). --- src/libsysprof-gtk/sysprof-callgraph-view.c | 56 +++++++------------- src/libsysprof-gtk/sysprof-callgraph-view.ui | 16 ++++++ 2 files changed, 35 insertions(+), 37 deletions(-) diff --git a/src/libsysprof-gtk/sysprof-callgraph-view.c b/src/libsysprof-gtk/sysprof-callgraph-view.c index 839d9d0b..776b5eda 100644 --- a/src/libsysprof-gtk/sysprof-callgraph-view.c +++ b/src/libsysprof-gtk/sysprof-callgraph-view.c @@ -143,29 +143,6 @@ callers_selection_changed_cb (SysprofCallgraphView *self, } } -static void -traceables_selection_changed_cb (SysprofCallgraphView *self, - guint position, - guint n_items, - GtkSingleSelection *single) -{ - GObject *object; - - g_assert (SYSPROF_IS_CALLGRAPH_VIEW (self)); - g_assert (GTK_IS_SINGLE_SELECTION (single)); - - gtk_column_view_set_model (self->traceable_column_view, NULL); - - if ((object = gtk_single_selection_get_selected_item (single))) - { - SysprofDocumentTraceable *traceable = SYSPROF_DOCUMENT_TRACEABLE (object); - g_autoptr(GListModel) model = sysprof_document_list_symbols_in_traceable (self->document, traceable); - g_autoptr(GtkNoSelection) no = gtk_no_selection_new (g_object_ref (model)); - - gtk_column_view_set_model (self->traceable_column_view, GTK_SELECTION_MODEL (no)); - } -} - static void sysprof_callgraph_view_list_traceables_cb (GObject *object, GAsyncResult *result, @@ -180,18 +157,7 @@ sysprof_callgraph_view_list_traceables_cb (GObject *object, g_assert (G_IS_ASYNC_RESULT (result)); g_assert (SYSPROF_IS_CALLGRAPH_VIEW (self)); - if ((model = sysprof_callgraph_frame_list_traceables_finish (frame, result, &error))) - { - g_autoptr(GtkSingleSelection) single = gtk_single_selection_new (g_object_ref (model)); - - g_signal_connect_object (single, - "selection-changed", - G_CALLBACK (traceables_selection_changed_cb), - self, - G_CONNECT_SWAPPED); - gtk_column_view_set_model (self->traceables_column_view, GTK_SELECTION_MODEL (single)); - traceables_selection_changed_cb (self, 0, 0, single); - } + model = sysprof_callgraph_frame_list_traceables_finish (frame, result, &error); sysprof_callgraph_view_set_utility_traceables (self, model); @@ -363,6 +329,21 @@ format_time_offset (gpointer cell) return g_strdup_printf ("%02d:%02d:%02.4lf", hours, minutes, time); } +static GListModel * +symbolize_traceable_cb (SysprofCallgraphView *self, + SysprofDocumentTraceable *traceable) +{ + SysprofDocument *document; + + g_assert (SYSPROF_IS_CALLGRAPH_VIEW (self)); + g_assert (!traceable || SYSPROF_IS_DOCUMENT_TRACEABLE (traceable)); + + if (traceable == NULL || !(document = sysprof_callgraph_view_get_document (self))) + return NULL; + + return sysprof_document_list_symbols_in_traceable (document, traceable); +} + static void sysprof_callgraph_view_dispose (GObject *object) { @@ -509,6 +490,7 @@ sysprof_callgraph_view_class_init (SysprofCallgraphViewClass *klass) gtk_widget_class_bind_template_child (widget_class, SysprofCallgraphView, traceables_column_view); gtk_widget_class_bind_template_callback (widget_class, format_time_offset); gtk_widget_class_bind_template_callback (widget_class, traceable_activate_cb); + gtk_widget_class_bind_template_callback (widget_class, symbolize_traceable_cb); klass->augment_size = GLIB_SIZEOF_VOID_P; @@ -650,11 +632,11 @@ sysprof_callgraph_view_queue_reload (SysprofCallgraphView *self) { g_assert (SYSPROF_IS_CALLGRAPH_VIEW (self)); + sysprof_callgraph_view_set_utility_traceables (self, NULL); + gtk_column_view_set_model (self->descendants_column_view, NULL); gtk_column_view_set_model (self->functions_column_view, NULL); gtk_column_view_set_model (self->callers_column_view, NULL); - gtk_column_view_set_model (self->traceables_column_view, NULL); - gtk_column_view_set_model (self->traceable_column_view, NULL); g_clear_handle_id (&self->reload_source, g_source_remove); g_cancellable_cancel (self->cancellable); diff --git a/src/libsysprof-gtk/sysprof-callgraph-view.ui b/src/libsysprof-gtk/sysprof-callgraph-view.ui index 85c75265..d6501cbc 100644 --- a/src/libsysprof-gtk/sysprof-callgraph-view.ui +++ b/src/libsysprof-gtk/sysprof-callgraph-view.ui @@ -232,6 +232,13 @@ + + + + SysprofCallgraphView + + + Time @@ -335,6 +342,15 @@ true + + + + + traceables_selection + + + + Stack Trace From ea8befb624d7c66daa09c822d281e9276325b5ae Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 12 Jul 2023 15:48:28 -0700 Subject: [PATCH 0836/1030] sysprof: move traceables utility into SyprofSamplesSection This gets it out of the callgrpah view so that it can live in the sidebar area of a AdwOverlayNavigationView. We allow the utility area to control the entire view (including the toolbarview) so that we don't have to bind extra properties for titles, and other random things. --- .../sysprof-callgraph-view-private.h | 3 - src/libsysprof-gtk/sysprof-callgraph-view.c | 59 +----- src/libsysprof-gtk/sysprof-callgraph-view.ui | 170 ------------------ src/sysprof/sysprof-samples-section.c | 2 + src/sysprof/sysprof-samples-section.ui | 23 +++ src/sysprof/sysprof-section.c | 48 ++++- src/sysprof/sysprof-section.h | 3 + src/sysprof/sysprof-traceables-utility.c | 53 +++++- src/sysprof/sysprof-traceables-utility.ui | 162 +++++++++++++++++ src/sysprof/sysprof-window.c | 34 ---- src/sysprof/sysprof-window.ui | 33 +--- 11 files changed, 293 insertions(+), 297 deletions(-) diff --git a/src/libsysprof-gtk/sysprof-callgraph-view-private.h b/src/libsysprof-gtk/sysprof-callgraph-view-private.h index 43df9c99..4efd72b6 100644 --- a/src/libsysprof-gtk/sysprof-callgraph-view-private.h +++ b/src/libsysprof-gtk/sysprof-callgraph-view-private.h @@ -40,12 +40,9 @@ struct _SysprofCallgraphView GtkColumnView *callers_column_view; GtkColumnView *descendants_column_view; GtkColumnView *functions_column_view; - GtkColumnView *traceables_column_view; - GtkColumnView *traceable_column_view; GtkCustomSorter *descendants_name_sorter; GtkCustomSorter *functions_name_sorter; GtkScrolledWindow *scrolled_window; - PanelPaned *right_paned; GtkWidget *paned; GCancellable *cancellable; diff --git a/src/libsysprof-gtk/sysprof-callgraph-view.c b/src/libsysprof-gtk/sysprof-callgraph-view.c index 776b5eda..d467ee18 100644 --- a/src/libsysprof-gtk/sysprof-callgraph-view.c +++ b/src/libsysprof-gtk/sysprof-callgraph-view.c @@ -160,9 +160,6 @@ sysprof_callgraph_view_list_traceables_cb (GObject *object, model = sysprof_callgraph_frame_list_traceables_finish (frame, result, &error); sysprof_callgraph_view_set_utility_traceables (self, model); - - gtk_widget_set_visible (gtk_widget_get_parent (GTK_WIDGET (self->right_paned)), - model && g_list_model_get_n_items (model)); } static void @@ -250,6 +247,7 @@ functions_selection_changed_cb (SysprofCallgraphView *self, } } +#if 0 static void traceable_activate_cb (SysprofCallgraphView *self, guint position, @@ -297,52 +295,7 @@ traceable_activate_cb (SysprofCallgraphView *self, } } } - -static char * -format_time_offset (gpointer cell) -{ - g_autoptr(SysprofDocumentFrame) frame = NULL; - int hours; - int minutes; - double time; - - g_object_get (cell, "item", &frame, NULL); - g_assert (!frame || SYSPROF_IS_DOCUMENT_FRAME (frame)); - - if (!frame) - return NULL; - - time = sysprof_document_frame_get_time_offset (frame) / (double)SYSPROF_NSEC_PER_SEC; - - hours = time / (60 * 60); - time -= hours * (60 * 60); - - minutes = time / 60; - time -= minutes * 60; - - if (hours == 0 && minutes == 0) - return g_strdup_printf ("%.4lf", time); - - if (hours == 0) - return g_strdup_printf ("%02d:%02.4lf", minutes, time); - - return g_strdup_printf ("%02d:%02d:%02.4lf", hours, minutes, time); -} - -static GListModel * -symbolize_traceable_cb (SysprofCallgraphView *self, - SysprofDocumentTraceable *traceable) -{ - SysprofDocument *document; - - g_assert (SYSPROF_IS_CALLGRAPH_VIEW (self)); - g_assert (!traceable || SYSPROF_IS_DOCUMENT_TRACEABLE (traceable)); - - if (traceable == NULL || !(document = sysprof_callgraph_view_get_document (self))) - return NULL; - - return sysprof_document_list_symbols_in_traceable (document, traceable); -} +#endif static void sysprof_callgraph_view_dispose (GObject *object) @@ -485,12 +438,6 @@ sysprof_callgraph_view_class_init (SysprofCallgraphViewClass *klass) gtk_widget_class_bind_template_child (widget_class, SysprofCallgraphView, functions_column_view); gtk_widget_class_bind_template_child (widget_class, SysprofCallgraphView, functions_name_sorter); gtk_widget_class_bind_template_child (widget_class, SysprofCallgraphView, paned); - gtk_widget_class_bind_template_child (widget_class, SysprofCallgraphView, right_paned); - gtk_widget_class_bind_template_child (widget_class, SysprofCallgraphView, traceable_column_view); - gtk_widget_class_bind_template_child (widget_class, SysprofCallgraphView, traceables_column_view); - gtk_widget_class_bind_template_callback (widget_class, format_time_offset); - gtk_widget_class_bind_template_callback (widget_class, traceable_activate_cb); - gtk_widget_class_bind_template_callback (widget_class, symbolize_traceable_cb); klass->augment_size = GLIB_SIZEOF_VOID_P; @@ -504,8 +451,6 @@ static void sysprof_callgraph_view_init (SysprofCallgraphView *self) { gtk_widget_init_template (GTK_WIDGET (self)); - - gtk_widget_set_visible (gtk_widget_get_parent (GTK_WIDGET (self->right_paned)), FALSE); } static int diff --git a/src/libsysprof-gtk/sysprof-callgraph-view.ui b/src/libsysprof-gtk/sysprof-callgraph-view.ui index d6501cbc..525b6f00 100644 --- a/src/libsysprof-gtk/sysprof-callgraph-view.ui +++ b/src/libsysprof-gtk/sysprof-callgraph-view.ui @@ -220,176 +220,6 @@ - - - false - vertical - - - 100 - - - - - - - SysprofCallgraphView - - - - - - Time - - - - - - -]]> - - - - - - - PID - - - - - - -]]> - - - - - - - Depth - true - - - - - - -]]> - - - - - - - - - - - true - true - - - - - true - - - - - traceables_selection - - - - - - - Stack Trace - true - - - - - - -]]> - - - - - - - - - - diff --git a/src/sysprof/sysprof-samples-section.c b/src/sysprof/sysprof-samples-section.c index 71fe3e6a..c1f7758b 100644 --- a/src/sysprof/sysprof-samples-section.c +++ b/src/sysprof/sysprof-samples-section.c @@ -23,6 +23,7 @@ #include #include "sysprof-samples-section.h" +#include "sysprof-traceables-utility.h" struct _SysprofSamplesSection { @@ -57,6 +58,7 @@ sysprof_samples_section_class_init (SysprofSamplesSectionClass *klass) g_type_ensure (SYSPROF_TYPE_CHART); g_type_ensure (SYSPROF_TYPE_XY_SERIES); g_type_ensure (SYSPROF_TYPE_COLUMN_LAYER); + g_type_ensure (SYSPROF_TYPE_TRACEABLES_UTILITY); g_type_ensure (SYSPROF_TYPE_VALUE_AXIS); g_type_ensure (SYSPROF_TYPE_WEIGHTED_CALLGRAPH_VIEW); } diff --git a/src/sysprof/sysprof-samples-section.ui b/src/sysprof/sysprof-samples-section.ui index 8b459290..be150971 100644 --- a/src/sysprof/sysprof-samples-section.ui +++ b/src/sysprof/sysprof-samples-section.ui @@ -73,5 +73,28 @@ + + + + + + + Stack Trace + + + + + + + + SysprofSamplesSection + + + callgraph_view + + + + + diff --git a/src/sysprof/sysprof-section.c b/src/sysprof/sysprof-section.c index 581edded..833c41c2 100644 --- a/src/sysprof/sysprof-section.c +++ b/src/sysprof/sysprof-section.c @@ -25,10 +25,11 @@ typedef struct { - char *category; - char *icon_name; - char *title; + char *category; + char *icon_name; + char *title; SysprofSession *session; + GtkWidget *utility; } SysprofSectionPrivate; G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (SysprofSection, sysprof_section, GTK_TYPE_WIDGET) @@ -39,6 +40,7 @@ enum { PROP_CATEGORY, PROP_ICON_NAME, PROP_TITLE, + PROP_UTILITY, N_PROPS }; @@ -54,6 +56,7 @@ sysprof_section_dispose (GObject *object) while ((child = gtk_widget_get_first_child (GTK_WIDGET (self)))) gtk_widget_unparent (child); + g_clear_object (&priv->utility); g_clear_object (&priv->session); g_clear_pointer (&priv->title, g_free); g_clear_pointer (&priv->category, g_free); @@ -88,6 +91,10 @@ sysprof_section_get_property (GObject *object, g_value_set_string (value, sysprof_section_get_title (self)); break; + case PROP_UTILITY: + g_value_set_object (value, sysprof_section_get_utility (self)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } @@ -119,6 +126,10 @@ sysprof_section_set_property (GObject *object, sysprof_section_set_title (self, g_value_get_string (value)); break; + case PROP_UTILITY: + sysprof_section_set_utility (self, g_value_get_object (value)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } @@ -154,6 +165,11 @@ sysprof_section_class_init (SysprofSectionClass *klass) NULL, (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); + properties[PROP_UTILITY] = + g_param_spec_object ("utility", NULL, NULL, + GTK_TYPE_WIDGET, + (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_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT); @@ -252,3 +268,29 @@ sysprof_section_set_icon_name (SysprofSection *self, if (g_set_str (&priv->icon_name, icon_name)) g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ICON_NAME]); } + +GtkWidget * +sysprof_section_get_utility (SysprofSection *self) +{ + SysprofSectionPrivate *priv = sysprof_section_get_instance_private (self); + + g_return_val_if_fail (SYSPROF_IS_SECTION (self), NULL); + + return priv->utility; +} + +void +sysprof_section_set_utility (SysprofSection *self, + GtkWidget *utility) +{ + SysprofSectionPrivate *priv = sysprof_section_get_instance_private (self); + g_autoptr(GtkWidget) hold = NULL; + + g_return_if_fail (SYSPROF_IS_SECTION (self)); + g_return_if_fail (!utility || GTK_IS_WIDGET (utility)); + + g_set_object (&hold, priv->utility); + + if (g_set_object (&priv->utility, utility)) + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_UTILITY]); +} diff --git a/src/sysprof/sysprof-section.h b/src/sysprof/sysprof-section.h index edc9e464..4140cebb 100644 --- a/src/sysprof/sysprof-section.h +++ b/src/sysprof/sysprof-section.h @@ -47,5 +47,8 @@ void sysprof_section_set_icon_name (SysprofSection *self, const char *sysprof_section_get_title (SysprofSection *self); void sysprof_section_set_title (SysprofSection *self, const char *title); +GtkWidget *sysprof_section_get_utility (SysprofSection *self); +void sysprof_section_set_utility (SysprofSection *self, + GtkWidget *utility); G_END_DECLS diff --git a/src/sysprof/sysprof-traceables-utility.c b/src/sysprof/sysprof-traceables-utility.c index a3cb5f6e..f7406343 100644 --- a/src/sysprof/sysprof-traceables-utility.c +++ b/src/sysprof/sysprof-traceables-utility.c @@ -42,6 +42,53 @@ G_DEFINE_FINAL_TYPE (SysprofTraceablesUtility, sysprof_traceables_utility, GTK_T static GParamSpec *properties[N_PROPS]; +static char * +format_time_offset (gpointer cell) +{ + g_autoptr(SysprofDocumentFrame) frame = NULL; + int hours; + int minutes; + double time; + + g_object_get (cell, "item", &frame, NULL); + g_assert (!frame || SYSPROF_IS_DOCUMENT_FRAME (frame)); + + if (!frame) + return NULL; + + time = sysprof_document_frame_get_time_offset (frame) / (double)SYSPROF_NSEC_PER_SEC; + + hours = time / (60 * 60); + time -= hours * (60 * 60); + + minutes = time / 60; + time -= minutes * 60; + + if (hours == 0 && minutes == 0) + return g_strdup_printf ("%.4lf", time); + + if (hours == 0) + return g_strdup_printf ("%02d:%02.4lf", minutes, time); + + return g_strdup_printf ("%02d:%02d:%02.4lf", hours, minutes, time); +} + +static GListModel * +symbolize_traceable (SysprofTraceablesUtility *self, + SysprofDocumentTraceable *traceable) +{ + SysprofDocument *document; + + g_assert (SYSPROF_IS_TRACEABLES_UTILITY (self)); + g_assert (!traceable || SYSPROF_IS_DOCUMENT_TRACEABLE (traceable)); + + if (traceable == NULL || self->session == NULL || + !(document = sysprof_session_get_document (self->session))) + return NULL; + + return sysprof_document_list_symbols_in_traceable (document, traceable); +} + static void sysprof_traceables_utility_finalize (GObject *object) { @@ -117,17 +164,19 @@ sysprof_traceables_utility_class_init (SysprofTraceablesUtilityClass *klass) properties[PROP_SESSION] = g_param_spec_object ("session", NULL, NULL, SYSPROF_TYPE_SESSION, - (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); + (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); properties[PROP_TRACEABLES] = g_param_spec_object ("traceables", NULL, NULL, G_TYPE_LIST_MODEL, - (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); + (G_PARAM_READWRITE | 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-traceables-utility.ui"); gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT); + gtk_widget_class_bind_template_callback (widget_class, format_time_offset); + gtk_widget_class_bind_template_callback (widget_class, symbolize_traceable); } static void diff --git a/src/sysprof/sysprof-traceables-utility.ui b/src/sysprof/sysprof-traceables-utility.ui index 7aadec01..077bd3e5 100644 --- a/src/sysprof/sysprof-traceables-utility.ui +++ b/src/sysprof/sysprof-traceables-utility.ui @@ -4,6 +4,168 @@ vertical + + + + + + + + + SysprofTraceablesUtility + + + + + + Time + + + + + + +]]> + + + + + + + PID + + + + + + +]]> + + + + + + + Depth + true + + + + + + +]]> + + + + + + + + + + + true + + + + + true + + + + + traceables_selection + + + + + + + Stack Trace + true + + + + + + + ]]> + + + + + + + + diff --git a/src/sysprof/sysprof-window.c b/src/sysprof/sysprof-window.c index f7313fa0..5f54e8e1 100644 --- a/src/sysprof/sysprof-window.c +++ b/src/sysprof/sysprof-window.c @@ -33,7 +33,6 @@ #include "sysprof-processes-section.h" #include "sysprof-samples-section.h" #include "sysprof-sidebar.h" -#include "sysprof-traceables-utility.h" #include "sysprof-window.h" struct _SysprofWindow @@ -42,9 +41,6 @@ struct _SysprofWindow SysprofDocument *document; SysprofSession *session; - - AdwViewStack *utility_stack; - AdwWindowTitle *utility_title; }; enum { @@ -197,9 +193,6 @@ sysprof_window_class_init (SysprofWindowClass *klass) gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/sysprof/sysprof-window.ui"); - gtk_widget_class_bind_template_child (widget_class, SysprofWindow, utility_stack); - gtk_widget_class_bind_template_child (widget_class, SysprofWindow, utility_title); - gtk_widget_class_install_action (widget_class, "win.open-capture", NULL, sysprof_window_open_capture_action); gtk_widget_class_install_action (widget_class, "win.record-capture", NULL, sysprof_window_record_capture_action); @@ -213,39 +206,12 @@ sysprof_window_class_init (SysprofWindowClass *klass) g_type_ensure (SYSPROF_TYPE_SAMPLES_SECTION); g_type_ensure (SYSPROF_TYPE_SESSION); g_type_ensure (SYSPROF_TYPE_SIDEBAR); - g_type_ensure (SYSPROF_TYPE_TRACEABLES_UTILITY); -} - -static void -utility_stack_notify_visible_child_cb (SysprofWindow *self, - GParamSpec *pspec, - AdwViewStack *utility_stack) -{ - AdwViewStackPage *page; - const char *title = NULL; - GtkWidget *child; - - g_assert (SYSPROF_IS_WINDOW (self)); - g_assert (ADW_IS_VIEW_STACK (utility_stack)); - - if ((child = adw_view_stack_get_visible_child (utility_stack)) && - (page = adw_view_stack_get_page (utility_stack, child))) - title = adw_view_stack_page_get_title (page); - - adw_window_title_set_title (self->utility_title, title); } static void sysprof_window_init (SysprofWindow *self) { gtk_widget_init_template (GTK_WIDGET (self)); - - g_signal_connect_object (self->utility_stack, - "notify::visible-child", - G_CALLBACK (utility_stack_notify_visible_child_cb), - self, - G_CONNECT_SWAPPED); - utility_stack_notify_visible_child_cb (self, NULL, self->utility_stack); } GtkWidget * diff --git a/src/sysprof/sysprof-window.ui b/src/sysprof/sysprof-window.ui index ae6d3251..d74646bb 100644 --- a/src/sysprof/sysprof-window.ui +++ b/src/sysprof/sysprof-window.ui @@ -164,34 +164,11 @@ - - - - - - - - - - - - true - - - Stack Traces - - - - SysprofWindow - - - - - - - - - + + + section_stack + + From f3418c42e92dc75738996f8cda26543e0fd8386d Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 12 Jul 2023 15:53:38 -0700 Subject: [PATCH 0837/1030] sysprof: hide utility when there is no sidebar widget --- src/sysprof/sysprof-window.c | 14 ++++++++++++++ src/sysprof/sysprof-window.ui | 1 + 2 files changed, 15 insertions(+) diff --git a/src/sysprof/sysprof-window.c b/src/sysprof/sysprof-window.c index 5f54e8e1..1e60f4ee 100644 --- a/src/sysprof/sysprof-window.c +++ b/src/sysprof/sysprof-window.c @@ -114,6 +114,18 @@ sysprof_window_set_document (SysprofWindow *self, } } +static void +main_view_notify_sidebar (SysprofWindow *self, + GParamSpec *pspec, + AdwOverlaySplitView *main_view) +{ + g_assert (SYSPROF_IS_WINDOW (self)); + g_assert (ADW_IS_OVERLAY_SPLIT_VIEW (main_view)); + + if (adw_overlay_split_view_get_sidebar (main_view) == NULL) + adw_overlay_split_view_set_show_sidebar (main_view, FALSE); +} + static void sysprof_window_dispose (GObject *object) { @@ -193,6 +205,8 @@ sysprof_window_class_init (SysprofWindowClass *klass) gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/sysprof/sysprof-window.ui"); + gtk_widget_class_bind_template_callback (widget_class, main_view_notify_sidebar); + gtk_widget_class_install_action (widget_class, "win.open-capture", NULL, sysprof_window_open_capture_action); gtk_widget_class_install_action (widget_class, "win.record-capture", NULL, sysprof_window_record_capture_action); diff --git a/src/sysprof/sysprof-window.ui b/src/sysprof/sysprof-window.ui index d74646bb..3cd9a532 100644 --- a/src/sysprof/sysprof-window.ui +++ b/src/sysprof/sysprof-window.ui @@ -62,6 +62,7 @@ end 200 + From d0012dd3b14d1832daa67ccf4022f6d837e99d35 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 12 Jul 2023 16:02:45 -0700 Subject: [PATCH 0838/1030] sysprof: make the sidebar a bit nicer w/ traceables --- src/libsysprof-gtk/style.css | 4 ++++ src/sysprof/sysprof-samples-section.ui | 3 +++ 2 files changed, 7 insertions(+) diff --git a/src/libsysprof-gtk/style.css b/src/libsysprof-gtk/style.css index 09108324..77fd213c 100644 --- a/src/libsysprof-gtk/style.css +++ b/src/libsysprof-gtk/style.css @@ -99,3 +99,7 @@ tracks row { tracks row track info { padding: 1px 0 0 0; } + +.utility .view { + background: transparent; +} diff --git a/src/sysprof/sysprof-samples-section.ui b/src/sysprof/sysprof-samples-section.ui index be150971..b3cdb02b 100644 --- a/src/sysprof/sysprof-samples-section.ui +++ b/src/sysprof/sysprof-samples-section.ui @@ -75,6 +75,9 @@ + From 03bc678daac98325aabf58fa0ac70991e7da161d Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 12 Jul 2023 16:45:42 -0700 Subject: [PATCH 0839/1030] sysprof: add dialog to show CPU information --- src/sysprof/meson.build | 1 + src/sysprof/sysprof-cpu-info-dialog.c | 135 +++++++++++++++++++++++++ src/sysprof/sysprof-cpu-info-dialog.h | 37 +++++++ src/sysprof/sysprof-cpu-info-dialog.ui | 121 ++++++++++++++++++++++ src/sysprof/sysprof-window.c | 20 ++++ src/sysprof/sysprof-window.ui | 6 ++ src/sysprof/sysprof.gresource.xml | 1 + 7 files changed, 321 insertions(+) create mode 100644 src/sysprof/sysprof-cpu-info-dialog.c create mode 100644 src/sysprof/sysprof-cpu-info-dialog.h create mode 100644 src/sysprof/sysprof-cpu-info-dialog.ui diff --git a/src/sysprof/meson.build b/src/sysprof/meson.build index d971be62..1eb06e08 100644 --- a/src/sysprof/meson.build +++ b/src/sysprof/meson.build @@ -1,6 +1,7 @@ sysprof_sources = [ 'main.c', 'sysprof-application.c', + 'sysprof-cpu-info-dialog.c', 'sysprof-files-section.c', 'sysprof-greeter.c', 'sysprof-logs-section.c', diff --git a/src/sysprof/sysprof-cpu-info-dialog.c b/src/sysprof/sysprof-cpu-info-dialog.c new file mode 100644 index 00000000..eabe36e6 --- /dev/null +++ b/src/sysprof/sysprof-cpu-info-dialog.c @@ -0,0 +1,135 @@ +/* sysprof-cpu-info-dialog.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-cpu-info-dialog.h" + +struct _SysprofCpuInfoDialog +{ + AdwWindow parent_instance; + SysprofDocument *document; +}; + +enum { + PROP_0, + PROP_DOCUMENT, + N_PROPS +}; + +G_DEFINE_FINAL_TYPE (SysprofCpuInfoDialog, sysprof_cpu_info_dialog, ADW_TYPE_WINDOW) + +static GParamSpec *properties [N_PROPS]; + +static void +sysprof_cpu_info_dialog_dispose (GObject *object) +{ + SysprofCpuInfoDialog *self = (SysprofCpuInfoDialog *)object; + + gtk_widget_dispose_template (GTK_WIDGET (self), SYSPROF_TYPE_CPU_INFO_DIALOG); + + g_clear_object (&self->document); + + G_OBJECT_CLASS (sysprof_cpu_info_dialog_parent_class)->dispose (object); +} + +static void +sysprof_cpu_info_dialog_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofCpuInfoDialog *self = SYSPROF_CPU_INFO_DIALOG (object); + + switch (prop_id) + { + case PROP_DOCUMENT: + g_value_set_object (value, sysprof_cpu_info_dialog_get_document (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_cpu_info_dialog_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SysprofCpuInfoDialog *self = SYSPROF_CPU_INFO_DIALOG (object); + + switch (prop_id) + { + case PROP_DOCUMENT: + sysprof_cpu_info_dialog_set_document (self, g_value_get_object (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_cpu_info_dialog_class_init (SysprofCpuInfoDialogClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->dispose = sysprof_cpu_info_dialog_dispose; + object_class->get_property = sysprof_cpu_info_dialog_get_property; + object_class->set_property = sysprof_cpu_info_dialog_set_property; + + properties [PROP_DOCUMENT] = + g_param_spec_object ("document", NULL, NULL, + SYSPROF_TYPE_DOCUMENT, + (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-cpu-info-dialog.ui"); +} + +static void +sysprof_cpu_info_dialog_init (SysprofCpuInfoDialog *self) +{ + gtk_widget_init_template (GTK_WIDGET (self)); +} + +SysprofDocument * +sysprof_cpu_info_dialog_get_document (SysprofCpuInfoDialog *self) +{ + g_return_val_if_fail (SYSPROF_IS_CPU_INFO_DIALOG (self), NULL); + + return self->document; +} + +void +sysprof_cpu_info_dialog_set_document (SysprofCpuInfoDialog *self, + SysprofDocument *document) +{ + g_return_if_fail (SYSPROF_IS_CPU_INFO_DIALOG (self)); + g_return_if_fail (!document || SYSPROF_IS_DOCUMENT (document)); + + if (g_set_object (&self->document, document)) + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DOCUMENT]); +} + diff --git a/src/sysprof/sysprof-cpu-info-dialog.h b/src/sysprof/sysprof-cpu-info-dialog.h new file mode 100644 index 00000000..5d1b3d1f --- /dev/null +++ b/src/sysprof/sysprof-cpu-info-dialog.h @@ -0,0 +1,37 @@ +/* sysprof-cpu-info-dialog.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +#include + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_CPU_INFO_DIALOG (sysprof_cpu_info_dialog_get_type()) + +G_DECLARE_FINAL_TYPE (SysprofCpuInfoDialog, sysprof_cpu_info_dialog, SYSPROF, CPU_INFO_DIALOG, AdwWindow) + +SysprofDocument *sysprof_cpu_info_dialog_get_document (SysprofCpuInfoDialog *self); +void sysprof_cpu_info_dialog_set_document (SysprofCpuInfoDialog *self, + SysprofDocument *document); + +G_END_DECLS diff --git a/src/sysprof/sysprof-cpu-info-dialog.ui b/src/sysprof/sysprof-cpu-info-dialog.ui new file mode 100644 index 00000000..decb5530 --- /dev/null +++ b/src/sysprof/sysprof-cpu-info-dialog.ui @@ -0,0 +1,121 @@ + + + + + diff --git a/src/sysprof/sysprof-window.c b/src/sysprof/sysprof-window.c index 1e60f4ee..c8981ad8 100644 --- a/src/sysprof/sysprof-window.c +++ b/src/sysprof/sysprof-window.c @@ -24,6 +24,7 @@ #include +#include "sysprof-cpu-info-dialog.h" #include "sysprof-files-section.h" #include "sysprof-greeter.h" #include "sysprof-logs-section.h" @@ -85,6 +86,24 @@ sysprof_window_record_capture_action (GtkWidget *widget, show_greeter (SYSPROF_WINDOW (widget), SYSPROF_GREETER_PAGE_RECORD); } +static void +sysprof_window_show_cpu_info_action (GtkWidget *widget, + const char *action_name, + GVariant *param) +{ + SysprofWindow *self = (SysprofWindow *)widget; + GtkWindow *window; + + g_assert (SYSPROF_IS_WINDOW (self)); + + window = g_object_new (SYSPROF_TYPE_CPU_INFO_DIALOG, + "document", self->document, + "transient-for", self, + NULL); + + gtk_window_present (window); +} + static void sysprof_window_set_document (SysprofWindow *self, SysprofDocument *document) @@ -209,6 +228,7 @@ sysprof_window_class_init (SysprofWindowClass *klass) gtk_widget_class_install_action (widget_class, "win.open-capture", NULL, sysprof_window_open_capture_action); gtk_widget_class_install_action (widget_class, "win.record-capture", NULL, sysprof_window_record_capture_action); + gtk_widget_class_install_action (widget_class, "win.show-cpu-info", NULL, sysprof_window_show_cpu_info_action); g_type_ensure (SYSPROF_TYPE_DOCUMENT); g_type_ensure (SYSPROF_TYPE_FILES_SECTION); diff --git a/src/sysprof/sysprof-window.ui b/src/sysprof/sysprof-window.ui index 3cd9a532..17cb19a0 100644 --- a/src/sysprof/sysprof-window.ui +++ b/src/sysprof/sysprof-window.ui @@ -191,6 +191,12 @@ Save As… +
+ + Show CPU Information… + win.show-cpu-info + +
Preferences diff --git a/src/sysprof/sysprof.gresource.xml b/src/sysprof/sysprof.gresource.xml index 0e49f338..11777e93 100644 --- a/src/sysprof/sysprof.gresource.xml +++ b/src/sysprof/sysprof.gresource.xml @@ -3,6 +3,7 @@ gtk/help-overlay.ui gtk/menus.ui + sysprof-cpu-info-dialog.ui sysprof-files-section.ui sysprof-greeter.ui sysprof-logs-section.ui From 83e7579460742829d9d25819abdf585933fe5e13 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 12 Jul 2023 17:26:56 -0700 Subject: [PATCH 0840/1030] sysprof: start on SysprofTimeScrubber This is going to be used to bring the whole tracksview thing we did before and make the selections work directly on charts. We can probably use the same thing w/ a GtkOverlay and custom positioning to replace the old usage too. --- src/sysprof/meson.build | 1 + src/sysprof/sysprof-samples-section.c | 2 + src/sysprof/sysprof-samples-section.ui | 67 +++++---- src/sysprof/sysprof-time-scrubber.c | 186 +++++++++++++++++++++++++ src/sysprof/sysprof-time-scrubber.h | 38 +++++ src/sysprof/sysprof-time-scrubber.ui | 22 +++ src/sysprof/sysprof.gresource.xml | 1 + 7 files changed, 289 insertions(+), 28 deletions(-) create mode 100644 src/sysprof/sysprof-time-scrubber.c create mode 100644 src/sysprof/sysprof-time-scrubber.h create mode 100644 src/sysprof/sysprof-time-scrubber.ui diff --git a/src/sysprof/meson.build b/src/sysprof/meson.build index 1eb06e08..d4e30ae1 100644 --- a/src/sysprof/meson.build +++ b/src/sysprof/meson.build @@ -15,6 +15,7 @@ sysprof_sources = [ 'sysprof-section.c', 'sysprof-sidebar.c', 'sysprof-single-model.c', + 'sysprof-time-scrubber.c', 'sysprof-traceables-utility.c', 'sysprof-window.c', ] diff --git a/src/sysprof/sysprof-samples-section.c b/src/sysprof/sysprof-samples-section.c index c1f7758b..614423fb 100644 --- a/src/sysprof/sysprof-samples-section.c +++ b/src/sysprof/sysprof-samples-section.c @@ -24,6 +24,7 @@ #include "sysprof-samples-section.h" #include "sysprof-traceables-utility.h" +#include "sysprof-time-scrubber.h" struct _SysprofSamplesSection { @@ -58,6 +59,7 @@ sysprof_samples_section_class_init (SysprofSamplesSectionClass *klass) g_type_ensure (SYSPROF_TYPE_CHART); g_type_ensure (SYSPROF_TYPE_XY_SERIES); g_type_ensure (SYSPROF_TYPE_COLUMN_LAYER); + g_type_ensure (SYSPROF_TYPE_TIME_SCRUBBER); g_type_ensure (SYSPROF_TYPE_TRACEABLES_UTILITY); g_type_ensure (SYSPROF_TYPE_VALUE_AXIS); g_type_ensure (SYSPROF_TYPE_WEIGHTED_CALLGRAPH_VIEW); diff --git a/src/sysprof/sysprof-samples-section.ui b/src/sysprof/sysprof-samples-section.ui index b3cdb02b..ce780081 100644 --- a/src/sysprof/sysprof-samples-section.ui +++ b/src/sysprof/sysprof-samples-section.ui @@ -6,43 +6,54 @@ vertical - - 32 - - - Stack Traces - - - SysprofSamplesSection - - - - - 0 - 128 - - - - - - - - SysprofSamplesSection - + + + SysprofSamplesSection + + + + 32 + + + Stack Traces + + + SysprofSamplesSection - - + + + 0 + 128 + - - + + + + + + SysprofSamplesSection + + + + + + + + + + - + + + + + true diff --git a/src/sysprof/sysprof-time-scrubber.c b/src/sysprof/sysprof-time-scrubber.c new file mode 100644 index 00000000..bd14fefe --- /dev/null +++ b/src/sysprof/sysprof-time-scrubber.c @@ -0,0 +1,186 @@ +/* sysprof-time-scrubber.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include + +#include "sysprof-time-scrubber.h" + +struct _SysprofTimeScrubber +{ + GtkWidget parent_instance; + + SysprofSession *session; + + SysprofTimeRuler *ruler; + GtkBox *vbox; +}; + +enum { + PROP_0, + PROP_SESSION, + N_PROPS +}; + +static void buildable_iface_init (GtkBuildableIface *iface); +static GtkBuildableIface *buildable_parent; + +G_DEFINE_FINAL_TYPE_WITH_CODE (SysprofTimeScrubber, sysprof_time_scrubber, GTK_TYPE_WIDGET, + G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, buildable_iface_init)) + +static GParamSpec *properties[N_PROPS]; + +static void +sysprof_time_scrubber_dispose (GObject *object) +{ + SysprofTimeScrubber *self = (SysprofTimeScrubber *)object; + GtkWidget *child; + + gtk_widget_dispose_template (GTK_WIDGET (self), SYSPROF_TYPE_TIME_SCRUBBER); + + while ((child = gtk_widget_get_first_child (GTK_WIDGET (self)))) + gtk_widget_unparent (child); + + g_clear_object (&self->session); + + G_OBJECT_CLASS (sysprof_time_scrubber_parent_class)->dispose (object); +} + +static void +sysprof_time_scrubber_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofTimeScrubber *self = SYSPROF_TIME_SCRUBBER (object); + + switch (prop_id) + { + case PROP_SESSION: + g_value_set_object (value, sysprof_time_scrubber_get_session (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_time_scrubber_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SysprofTimeScrubber *self = SYSPROF_TIME_SCRUBBER (object); + + switch (prop_id) + { + case PROP_SESSION: + sysprof_time_scrubber_set_session (self, g_value_get_object (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_time_scrubber_class_init (SysprofTimeScrubberClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->dispose = sysprof_time_scrubber_dispose; + object_class->get_property = sysprof_time_scrubber_get_property; + object_class->set_property = sysprof_time_scrubber_set_property; + + properties[PROP_SESSION] = + g_param_spec_object ("session", NULL, NULL, + SYSPROF_TYPE_SESSION, + (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-time-scrubber.ui"); + gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT); + gtk_widget_class_bind_template_child (widget_class, SysprofTimeScrubber, vbox); + gtk_widget_class_bind_template_child (widget_class, SysprofTimeScrubber, ruler); + + g_type_ensure (SYSPROF_TYPE_TIME_RULER); +} + +static void +sysprof_time_scrubber_init (SysprofTimeScrubber *self) +{ + gtk_widget_init_template (GTK_WIDGET (self)); +} + +SysprofSession * +sysprof_time_scrubber_get_session (SysprofTimeScrubber *self) +{ + g_return_val_if_fail (SYSPROF_IS_TIME_SCRUBBER (self), NULL); + + return self->session; +} + +void +sysprof_time_scrubber_set_session (SysprofTimeScrubber *self, + SysprofSession *session) +{ + g_return_if_fail (SYSPROF_IS_TIME_SCRUBBER (self)); + g_return_if_fail (!session || SYSPROF_IS_SESSION (session)); + + if (g_set_object (&self->session, session)) + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SESSION]); +} + +static void +sysprof_time_scrubber_add_chart (SysprofTimeScrubber *self, + GtkWidget *chart) +{ + g_assert (SYSPROF_IS_TIME_SCRUBBER (self)); + g_assert (GTK_IS_WIDGET (chart)); + + gtk_box_append (self->vbox, chart); +} + +static void +sysprof_time_scrubber_add_child (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *object, + const char *type) +{ + SysprofTimeScrubber *self = (SysprofTimeScrubber *)buildable; + + g_assert (SYSPROF_IS_TIME_SCRUBBER (self)); + + if (g_strcmp0 (type, "chart") == 0) + sysprof_time_scrubber_add_chart (self, GTK_WIDGET (object)); + else + buildable_parent->add_child (buildable, builder, object, type); +} + +static void +buildable_iface_init (GtkBuildableIface *iface) +{ + buildable_parent = g_type_interface_peek_parent (iface); + iface->add_child = sysprof_time_scrubber_add_child; +} diff --git a/src/sysprof/sysprof-time-scrubber.h b/src/sysprof/sysprof-time-scrubber.h new file mode 100644 index 00000000..0ae3b4fe --- /dev/null +++ b/src/sysprof/sysprof-time-scrubber.h @@ -0,0 +1,38 @@ +/* sysprof-time-scrubber.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +#include + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_TIME_SCRUBBER (sysprof_time_scrubber_get_type()) + +G_DECLARE_FINAL_TYPE (SysprofTimeScrubber, sysprof_time_scrubber, SYSPROF, TIME_SCRUBBER, GtkWidget) + +GtkWidget *sysprof_time_scrubber_new (void); +SysprofSession *sysprof_time_scrubber_get_session (SysprofTimeScrubber *self); +void sysprof_time_scrubber_set_session (SysprofTimeScrubber *self, + SysprofSession *session); + +G_END_DECLS diff --git a/src/sysprof/sysprof-time-scrubber.ui b/src/sysprof/sysprof-time-scrubber.ui new file mode 100644 index 00000000..6683ccab --- /dev/null +++ b/src/sysprof/sysprof-time-scrubber.ui @@ -0,0 +1,22 @@ + + + + diff --git a/src/sysprof/sysprof.gresource.xml b/src/sysprof/sysprof.gresource.xml index 11777e93..9d2cf7bd 100644 --- a/src/sysprof/sysprof.gresource.xml +++ b/src/sysprof/sysprof.gresource.xml @@ -15,6 +15,7 @@ sysprof-recording-pad.ui sysprof-samples-section.ui sysprof-sidebar.ui + sysprof-time-scrubber.ui sysprof-traceables-utility.ui sysprof-window.ui icons/scalable/actions/address-layout-symbolic.svg From 08cf2b1c17ce574559b0eda4735341f5432bab7b Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 12 Jul 2023 17:53:23 -0700 Subject: [PATCH 0841/1030] libsysprof-gtk: allow null track --- src/libsysprof-gtk/sysprof-session.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libsysprof-gtk/sysprof-session.c b/src/libsysprof-gtk/sysprof-session.c index e85932ec..b690fb54 100644 --- a/src/libsysprof-gtk/sysprof-session.c +++ b/src/libsysprof-gtk/sysprof-session.c @@ -457,11 +457,12 @@ _sysprof_session_describe (SysprofSession *self, g_autofree char *text = NULL; g_return_val_if_fail (SYSPROF_IS_SESSION (self), NULL); + g_return_val_if_fail (!track || SYSPROF_IS_TRACK (track), NULL); if (self->document == NULL) return NULL; - if ((text = _sysprof_track_format_item_for_display (track, item))) + if (track && (text = _sysprof_track_format_item_for_display (track, item))) return g_steal_pointer (&text); if (SYSPROF_IS_DOCUMENT_MARK (item)) From e9b0609033d57e444ba229524854a7697cd3d123 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 12 Jul 2023 17:53:40 -0700 Subject: [PATCH 0842/1030] sysprof: use visible range for chart We don't want it to update based on selection since that should match what is in the scrubber. --- src/sysprof/sysprof-samples-section.ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sysprof/sysprof-samples-section.ui b/src/sysprof/sysprof-samples-section.ui index ce780081..2f9e4c63 100644 --- a/src/sysprof/sysprof-samples-section.ui +++ b/src/sysprof/sysprof-samples-section.ui @@ -17,7 +17,7 @@ Stack Traces - + SysprofSamplesSection From 31536573f44562fa0b75c08bab25d80e08070632 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 12 Jul 2023 17:53:55 -0700 Subject: [PATCH 0843/1030] sysprof: implement scrubbing for samples view --- src/libsysprof-gtk/style.css | 2 + src/sysprof/sysprof-time-scrubber.c | 523 ++++++++++++++++++++++++++- src/sysprof/sysprof-time-scrubber.ui | 50 +++ 3 files changed, 571 insertions(+), 4 deletions(-) diff --git a/src/libsysprof-gtk/style.css b/src/libsysprof-gtk/style.css index 77fd213c..d02d805d 100644 --- a/src/libsysprof-gtk/style.css +++ b/src/libsysprof-gtk/style.css @@ -69,6 +69,8 @@ timeruler { color: alpha(currentColor, .8); } +timescrubber informative, +timescrubber timecode, tracks informative, tracks timecode { border-radius: 7px; diff --git a/src/sysprof/sysprof-time-scrubber.c b/src/sysprof/sysprof-time-scrubber.c index bd14fefe..2e7a2f42 100644 --- a/src/sysprof/sysprof-time-scrubber.c +++ b/src/sysprof/sysprof-time-scrubber.c @@ -22,6 +22,8 @@ #include +#include "sysprof-session-private.h" + #include "sysprof-time-scrubber.h" struct _SysprofTimeScrubber @@ -29,9 +31,24 @@ struct _SysprofTimeScrubber GtkWidget parent_instance; SysprofSession *session; + GSignalGroup *session_signals; - SysprofTimeRuler *ruler; + GtkLabel *informative; + GtkBox *main; + GtkLabel *timecode; GtkBox *vbox; + SysprofTimeRuler *ruler; + GtkButton *zoom; + + double motion_x; + double motion_y; + + double drag_start_x; + double drag_start_y; + double drag_offset_x; + double drag_offset_y; + + guint in_drag_selection : 1; }; enum { @@ -48,6 +65,468 @@ G_DEFINE_FINAL_TYPE_WITH_CODE (SysprofTimeScrubber, sysprof_time_scrubber, GTK_T static GParamSpec *properties[N_PROPS]; +static void +set_motion (SysprofTimeScrubber *self, + double x, + double y) +{ + gboolean timecode_visible = FALSE; + gboolean informative_visible = FALSE; + GtkWidget *pick; + int ruler_start; + + g_assert (SYSPROF_IS_TIME_SCRUBBER (self)); + + if (self->motion_x == x && self->motion_y == y) + return; + + self->motion_x = x; + self->motion_y = y; + + ruler_start = 0; + timecode_visible = x >= ruler_start; + + if (timecode_visible) + { + g_autofree char *str = sysprof_time_ruler_get_label_at_point (self->ruler, x - ruler_start); + gtk_label_set_label (self->timecode, str); + } + + pick = gtk_widget_pick (GTK_WIDGET (self), x, y, 0); + + if (pick != NULL) + { + SysprofChartLayer *layer = SYSPROF_CHART_LAYER (gtk_widget_get_ancestor (pick, SYSPROF_TYPE_CHART_LAYER)); + + if (layer != NULL) + { + g_autoptr(GObject) item = NULL; + g_autofree char *text = NULL; + double layer_x; + double layer_y; + + gtk_widget_translate_coordinates (GTK_WIDGET (self), + GTK_WIDGET (layer), + x, y, &layer_x, &layer_y); + + if ((item = sysprof_chart_layer_lookup_item (layer, layer_x, layer_y))) + text = _sysprof_session_describe (self->session, NULL, item); + + gtk_label_set_label (self->informative, text); + + informative_visible = text != NULL; + } + } + + gtk_widget_set_visible (GTK_WIDGET (self->timecode), timecode_visible); + gtk_widget_set_visible (GTK_WIDGET (self->informative), informative_visible); + + gtk_widget_queue_draw (GTK_WIDGET (self)); +} + +static void +sysprof_time_scrubber_motion_enter_cb (SysprofTimeScrubber *self, + double x, + double y, + GtkEventControllerMotion *motion) +{ + g_assert (SYSPROF_IS_TIME_SCRUBBER (self)); + g_assert (GTK_IS_EVENT_CONTROLLER_MOTION (motion)); + + set_motion (self, x, y); +} + +static void +sysprof_time_scrubber_motion_leave_cb (SysprofTimeScrubber *self, + GtkEventControllerMotion *motion) +{ + g_assert (SYSPROF_IS_TIME_SCRUBBER (self)); + g_assert (GTK_IS_EVENT_CONTROLLER_MOTION (motion)); + + set_motion (self, -1, -1); +} + +static void +sysprof_time_scrubber_motion_cb (SysprofTimeScrubber *self, + double x, + double y, + GtkEventControllerMotion *motion) +{ + g_assert (SYSPROF_IS_TIME_SCRUBBER (self)); + g_assert (GTK_IS_EVENT_CONTROLLER_MOTION (motion)); + + set_motion (self, x, y); +} + +static void +sysprof_time_scrubber_drag_begin_cb (SysprofTimeScrubber *self, + double start_x, + double start_y, + GtkGestureDrag *drag) +{ + graphene_rect_t zoom_area; + double x, y; + + g_assert (SYSPROF_IS_TIME_SCRUBBER (self)); + g_assert (GTK_IS_GESTURE_DRAG (drag)); + + gtk_widget_translate_coordinates (GTK_WIDGET (self->zoom), + GTK_WIDGET (self), + 0, 0, &x, &y); + zoom_area = GRAPHENE_RECT_INIT (x, y, + gtk_widget_get_width (GTK_WIDGET (self->zoom)), + gtk_widget_get_height (GTK_WIDGET (self->zoom))); + + if (start_x < 0 || + (gtk_widget_get_visible (GTK_WIDGET (self->zoom)) && + graphene_rect_contains_point (&zoom_area, &GRAPHENE_POINT_INIT (start_x, start_y)))) + { + gtk_gesture_set_state (GTK_GESTURE (drag), GTK_EVENT_SEQUENCE_DENIED); + return; + } + + self->drag_start_x = start_x; + self->drag_start_y = start_y; + self->drag_offset_x = 0; + self->drag_offset_y = 0; + + gtk_widget_set_visible (GTK_WIDGET (self->zoom), FALSE); + + self->in_drag_selection = TRUE; + + gtk_widget_queue_draw (GTK_WIDGET (self)); +} + +static void +sysprof_time_scrubber_drag_end_cb (SysprofTimeScrubber *self, + double offset_x, + double offset_y, + GtkGestureDrag *drag) +{ + int base_x; + + g_assert (SYSPROF_IS_TIME_SCRUBBER (self)); + g_assert (GTK_IS_GESTURE_DRAG (drag)); + + if (self->session == NULL) + goto cleanup; + + base_x = 0; + + if (self->drag_offset_x != .0) + { + graphene_rect_t selection; + graphene_rect_t area; + int width; + int height; + + width = gtk_widget_get_width (GTK_WIDGET (self)) - base_x; + height = gtk_widget_get_height (GTK_WIDGET (self)); + + area = GRAPHENE_RECT_INIT (base_x, 0, width, height); + selection = GRAPHENE_RECT_INIT (self->drag_start_x, + 0, + self->drag_offset_x, + height); + graphene_rect_normalize (&selection); + + if (graphene_rect_intersection (&area, &selection, &selection)) + { + double begin = (selection.origin.x - area.origin.x) / area.size.width; + double end = (selection.origin.x + selection.size.width - area.origin.x) / area.size.width; + const SysprofTimeSpan *visible = sysprof_session_get_visible_time (self->session); + gint64 visible_duration = visible->end_nsec - visible->begin_nsec; + SysprofTimeSpan to_select; + + to_select.begin_nsec = visible->begin_nsec + (begin * visible_duration); + to_select.end_nsec = visible->begin_nsec + (end * visible_duration); + + sysprof_session_select_time (self->session, &to_select); + } + } + else if (self->drag_start_x >= base_x) + { + sysprof_session_select_time (self->session, sysprof_session_get_visible_time (self->session)); + } + +cleanup: + self->drag_start_x = -1; + self->drag_start_y = -1; + self->drag_offset_x = 0; + self->drag_offset_y = 0; + + self->in_drag_selection = FALSE; + + gtk_widget_queue_draw (GTK_WIDGET (self)); +} + +static void +sysprof_time_scrubber_drag_update_cb (SysprofTimeScrubber *self, + double offset_x, + double offset_y, + GtkGestureDrag *drag) +{ + g_assert (SYSPROF_IS_TIME_SCRUBBER (self)); + g_assert (GTK_IS_GESTURE_DRAG (drag)); + + self->drag_offset_x = offset_x, + self->drag_offset_y = offset_y; + + gtk_widget_queue_draw (GTK_WIDGET (self)); +} + +static gboolean +get_selected_area (SysprofTimeScrubber *self, + graphene_rect_t *area, + graphene_rect_t *selection) +{ + const SysprofTimeSpan *selected; + const SysprofTimeSpan *visible; + SysprofTimeSpan relative; + gint64 time_duration; + double begin; + double end; + int base_x; + int width; + int height; + + g_assert (SYSPROF_IS_TIME_SCRUBBER (self)); + g_assert (area != NULL); + g_assert (selection != NULL); + + if (self->session == NULL) + return FALSE; + + base_x = 0; + width = gtk_widget_get_width (GTK_WIDGET (self)) - base_x; + height = gtk_widget_get_height (GTK_WIDGET (self)); + + *area = GRAPHENE_RECT_INIT (base_x, 0, width, height); + + if (self->in_drag_selection && self->drag_offset_x != .0) + { + *selection = GRAPHENE_RECT_INIT (self->drag_start_x, + 0, + self->drag_offset_x, + height); + graphene_rect_normalize (selection); + return graphene_rect_intersection (area, selection, selection); + } + + /* If selected range == visible range, then there is no selection */ + selected = sysprof_session_get_selected_time (self->session); + visible = sysprof_session_get_visible_time (self->session); + if (memcmp (selected, visible, sizeof *selected) == 0) + return FALSE; + + time_duration = sysprof_time_span_duration (*visible); + relative = sysprof_time_span_relative_to (*selected, visible->begin_nsec); + + begin = relative.begin_nsec / (double)time_duration; + end = relative.end_nsec / (double)time_duration; + + *selection = GRAPHENE_RECT_INIT (area->origin.x + (begin * width), + 0, + (end * width) - (begin * width), + area->size.height); + + return TRUE; +} + +static void +sysprof_time_scrubber_measure (GtkWidget *widget, + GtkOrientation orientation, + int for_size, + int *minimum, + int *natural, + int *minimum_baseline, + int *natural_baseline) +{ + SysprofTimeScrubber *self = (SysprofTimeScrubber *)widget; + + g_assert (SYSPROF_IS_TIME_SCRUBBER (self)); + + gtk_widget_measure (GTK_WIDGET (self->main), + orientation, + for_size, + minimum, + natural, + minimum_baseline, + natural_baseline); +} + +static void +sysprof_time_scrubber_size_allocate (GtkWidget *widget, + int width, + int height, + int baseline) +{ + SysprofTimeScrubber *self = (SysprofTimeScrubber *)widget; + graphene_rect_t area; + graphene_rect_t selection; + + g_assert (SYSPROF_IS_TIME_SCRUBBER (self)); + + gtk_widget_size_allocate (GTK_WIDGET (self->main), + &(GtkAllocation) {0, 0, width, height}, + baseline); + + if (get_selected_area (self, &area, &selection)) + { + graphene_point_t middle; + GtkRequisition min_req; + GtkRequisition nat_req; + + /* Position the zoom button in the center of the selected area */ + gtk_widget_get_preferred_size (GTK_WIDGET (self->zoom), &min_req, &nat_req); + graphene_rect_get_center (&selection, &middle); + gtk_widget_size_allocate (GTK_WIDGET (self->zoom), + &(GtkAllocation) { + middle.x - (min_req.width/2), + middle.y - (min_req.height/2), + min_req.width, + min_req.height + }, -1); + } + + if (gtk_widget_get_visible (GTK_WIDGET (self->timecode))) + { + GtkRequisition min_req; + GtkRequisition nat_req; + + gtk_widget_get_preferred_size (GTK_WIDGET (self->timecode), &min_req, &nat_req); + + if (self->motion_x + min_req.width < gtk_widget_get_width (GTK_WIDGET (self))) + gtk_widget_size_allocate (GTK_WIDGET (self->timecode), + &(GtkAllocation) { + self->motion_x, 0, + min_req.width, min_req.height + }, -1); + else + gtk_widget_size_allocate (GTK_WIDGET (self->timecode), + &(GtkAllocation) { + self->motion_x - min_req.width, 0, + min_req.width, min_req.height + }, -1); + } + + if (gtk_widget_get_visible (GTK_WIDGET (self->informative))) + { + GtkRequisition min_req; + GtkRequisition nat_req; + + gtk_widget_get_preferred_size (GTK_WIDGET (self->informative), &min_req, &nat_req); + + if (self->motion_x + min_req.width < gtk_widget_get_width (GTK_WIDGET (self))) + gtk_widget_size_allocate (GTK_WIDGET (self->informative), + &(GtkAllocation) { + self->motion_x, self->motion_y, + min_req.width, min_req.height + }, -1); + else + gtk_widget_size_allocate (GTK_WIDGET (self->informative), + &(GtkAllocation) { + self->motion_x - min_req.width, self->motion_y, + min_req.width, min_req.height + }, -1); + } +} + +static void +sysprof_time_scrubber_snapshot (GtkWidget *widget, + GtkSnapshot *snapshot) +{ + SysprofTimeScrubber *self = (SysprofTimeScrubber *)widget; + graphene_rect_t area; + graphene_rect_t selection; + GdkRGBA shadow_color; + GdkRGBA line_color; + GdkRGBA color; + + g_assert (SYSPROF_IS_TIME_SCRUBBER (self)); + g_assert (GTK_IS_SNAPSHOT (snapshot)); + + gtk_widget_snapshot_child (GTK_WIDGET (self), GTK_WIDGET (self->main), snapshot); + +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + { + GtkStyleContext *style_context = gtk_widget_get_style_context (GTK_WIDGET (self)); + gtk_style_context_get_color (style_context, &color); + + shadow_color = color; + shadow_color.alpha *= .1; + + line_color = color; + line_color.alpha *= .5; + } +G_GNUC_END_IGNORE_DEPRECATIONS + + if (get_selected_area (self, &area, &selection)) + { + gtk_snapshot_append_color (snapshot, + &shadow_color, + &GRAPHENE_RECT_INIT (area.origin.x, + area.origin.y, + selection.origin.x - area.origin.x, + area.size.height)); + + gtk_snapshot_append_color (snapshot, + &shadow_color, + &GRAPHENE_RECT_INIT (selection.origin.x + selection.size.width, + area.origin.y, + (area.origin.x + area.size.width) - (selection.origin.x + selection.size.width), + area.size.height)); + } + + if (self->motion_x != -1 && self->motion_y != -1 && self->motion_x > 0) + gtk_snapshot_append_color (snapshot, + &line_color, + &GRAPHENE_RECT_INIT (self->motion_x, 0, 1, + gtk_widget_get_height (GTK_WIDGET (self)))); + + if (gtk_widget_get_visible (GTK_WIDGET (self->zoom))) + gtk_widget_snapshot_child (GTK_WIDGET (self), GTK_WIDGET (self->zoom), snapshot); + + if (gtk_widget_get_visible (GTK_WIDGET (self->timecode))) + gtk_widget_snapshot_child (GTK_WIDGET (self), GTK_WIDGET (self->timecode), snapshot); + + if (gtk_widget_get_visible (GTK_WIDGET (self->informative))) + gtk_widget_snapshot_child (GTK_WIDGET (self), GTK_WIDGET (self->informative), snapshot); +} + +static void +notify_time_cb (SysprofTimeScrubber *self, + GParamSpec *pspec, + SysprofSession *session) +{ + const SysprofTimeSpan *visible; + const SysprofTimeSpan *selected; + gboolean button_visible; + + g_assert (SYSPROF_IS_TIME_SCRUBBER (self)); + g_assert (SYSPROF_IS_SESSION (session)); + + visible = sysprof_session_get_visible_time (session); + selected = sysprof_session_get_selected_time (session); + + button_visible = memcmp (visible, selected, sizeof *visible) != 0; + + gtk_widget_set_visible (GTK_WIDGET (self->zoom), button_visible); +} + +static void +sysprof_time_scrubber_zoom_to_selection (GtkWidget *widget, + const char *action_name, + GVariant *params) +{ + SysprofTimeScrubber *self = (SysprofTimeScrubber *)widget; + + g_assert (SYSPROF_IS_TIME_SCRUBBER (self)); + + if (self->session != NULL) + sysprof_session_zoom_to_selection (self->session); +} + static void sysprof_time_scrubber_dispose (GObject *object) { @@ -60,6 +539,7 @@ sysprof_time_scrubber_dispose (GObject *object) gtk_widget_unparent (child); g_clear_object (&self->session); + g_clear_object (&self->session_signals); G_OBJECT_CLASS (sysprof_time_scrubber_parent_class)->dispose (object); } @@ -112,6 +592,10 @@ sysprof_time_scrubber_class_init (SysprofTimeScrubberClass *klass) object_class->get_property = sysprof_time_scrubber_get_property; object_class->set_property = sysprof_time_scrubber_set_property; + widget_class->measure = sysprof_time_scrubber_measure; + widget_class->snapshot = sysprof_time_scrubber_snapshot; + widget_class->size_allocate = sysprof_time_scrubber_size_allocate; + properties[PROP_SESSION] = g_param_spec_object ("session", NULL, NULL, SYSPROF_TYPE_SESSION, @@ -120,9 +604,25 @@ sysprof_time_scrubber_class_init (SysprofTimeScrubberClass *klass) g_object_class_install_properties (object_class, N_PROPS, properties); gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/sysprof/sysprof-time-scrubber.ui"); - gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT); - gtk_widget_class_bind_template_child (widget_class, SysprofTimeScrubber, vbox); + + gtk_widget_class_set_css_name (widget_class, "timescrubber"); + + gtk_widget_class_bind_template_child (widget_class, SysprofTimeScrubber, informative); + gtk_widget_class_bind_template_child (widget_class, SysprofTimeScrubber, main); gtk_widget_class_bind_template_child (widget_class, SysprofTimeScrubber, ruler); + gtk_widget_class_bind_template_child (widget_class, SysprofTimeScrubber, timecode); + gtk_widget_class_bind_template_child (widget_class, SysprofTimeScrubber, vbox); + gtk_widget_class_bind_template_child (widget_class, SysprofTimeScrubber, zoom); + + gtk_widget_class_bind_template_callback (widget_class, sysprof_time_scrubber_motion_enter_cb); + gtk_widget_class_bind_template_callback (widget_class, sysprof_time_scrubber_motion_leave_cb); + gtk_widget_class_bind_template_callback (widget_class, sysprof_time_scrubber_motion_cb); + + gtk_widget_class_bind_template_callback (widget_class, sysprof_time_scrubber_drag_begin_cb); + gtk_widget_class_bind_template_callback (widget_class, sysprof_time_scrubber_drag_end_cb); + gtk_widget_class_bind_template_callback (widget_class, sysprof_time_scrubber_drag_update_cb); + + gtk_widget_class_install_action (widget_class, "zoom-to-selection", NULL, sysprof_time_scrubber_zoom_to_selection); g_type_ensure (SYSPROF_TYPE_TIME_RULER); } @@ -130,6 +630,18 @@ sysprof_time_scrubber_class_init (SysprofTimeScrubberClass *klass) static void sysprof_time_scrubber_init (SysprofTimeScrubber *self) { + self->session_signals = g_signal_group_new (SYSPROF_TYPE_SESSION); + g_signal_group_connect_object (self->session_signals, + "notify::selected-time", + G_CALLBACK (notify_time_cb), + self, + G_CONNECT_SWAPPED); + g_signal_group_connect_object (self->session_signals, + "notify::visible-time", + G_CALLBACK (notify_time_cb), + self, + G_CONNECT_SWAPPED); + gtk_widget_init_template (GTK_WIDGET (self)); } @@ -149,7 +661,10 @@ sysprof_time_scrubber_set_session (SysprofTimeScrubber *self, g_return_if_fail (!session || SYSPROF_IS_SESSION (session)); if (g_set_object (&self->session, session)) - g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SESSION]); + { + g_signal_group_set_target (self->session_signals, session); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SESSION]); + } } static void diff --git a/src/sysprof/sysprof-time-scrubber.ui b/src/sysprof/sysprof-time-scrubber.ui index 6683ccab..0188f29a 100644 --- a/src/sysprof/sysprof-time-scrubber.ui +++ b/src/sysprof/sysprof-time-scrubber.ui @@ -4,6 +4,22 @@ vertical + + + capture + + + + + + + + bubble + + + + + @@ -18,5 +34,39 @@ + + + zoom-to-selection + zoom-in-symbolic + center + center + false + + + + + + timecode + false + false + false + .5 + true + 10 + 10 + + + + + informative + false + false + false + 0 + true + + From 52090e1f7775c1404073edb105bd8d0df7cf5873 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 12 Jul 2023 18:02:10 -0700 Subject: [PATCH 0844/1030] libsysprof-analyze: add marks property --- src/libsysprof-analyze/sysprof-document.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index 5df46f5a..fb7a8324 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -115,6 +115,7 @@ enum { PROP_CPU_INFO, PROP_FILES, PROP_LOGS, + PROP_MARKS, PROP_METADATA, PROP_PROCESSES, PROP_SAMPLES, @@ -381,6 +382,10 @@ sysprof_document_get_property (GObject *object, g_value_take_object (value, sysprof_document_list_logs (self)); break; + case PROP_MARKS: + g_value_take_object (value, sysprof_document_list_marks (self)); + break; + case PROP_METADATA: g_value_take_object (value, sysprof_document_list_metadata (self)); break; @@ -439,6 +444,11 @@ sysprof_document_class_init (SysprofDocumentClass *klass) G_TYPE_LIST_MODEL, (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + properties [PROP_MARKS] = + g_param_spec_object ("marks", NULL, NULL, + G_TYPE_LIST_MODEL, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + properties [PROP_METADATA] = g_param_spec_object ("metadata", NULL, NULL, G_TYPE_LIST_MODEL, From af5e62a7efcf177b7e5c8965c3096a16ceb04670 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 12 Jul 2023 18:02:22 -0700 Subject: [PATCH 0845/1030] sysprof: add scrubber to marks page --- src/sysprof/sysprof-marks-section.c | 8 +++-- src/sysprof/sysprof-marks-section.ui | 45 ++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 3 deletions(-) diff --git a/src/sysprof/sysprof-marks-section.c b/src/sysprof/sysprof-marks-section.c index 3b18eba3..f3200090 100644 --- a/src/sysprof/sysprof-marks-section.c +++ b/src/sysprof/sysprof-marks-section.c @@ -29,7 +29,7 @@ struct _SysprofMarksSection SysprofSection parent_instance; SysprofMarkChart *mark_chart; - //SysprofMarkTable *mark_table; + SysprofMarkTable *mark_table; }; G_DEFINE_FINAL_TYPE (SysprofMarksSection, sysprof_marks_section, SYSPROF_TYPE_SECTION) @@ -54,11 +54,14 @@ sysprof_marks_section_class_init (SysprofMarksSectionClass *klass) gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/sysprof/sysprof-marks-section.ui"); gtk_widget_class_bind_template_child (widget_class, SysprofMarksSection, mark_chart); - //gtk_widget_class_bind_template_child (widget_class, SysprofMarksSection, mark_table); + gtk_widget_class_bind_template_child (widget_class, SysprofMarksSection, mark_table); + g_type_ensure (SYSPROF_TYPE_CHART); g_type_ensure (SYSPROF_TYPE_DOCUMENT_MARK); g_type_ensure (SYSPROF_TYPE_MARK_CHART); g_type_ensure (SYSPROF_TYPE_MARK_TABLE); + g_type_ensure (SYSPROF_TYPE_TIME_SERIES); + g_type_ensure (SYSPROF_TYPE_TIME_SPAN_LAYER); } static void @@ -66,4 +69,3 @@ sysprof_marks_section_init (SysprofMarksSection *self) { gtk_widget_init_template (GTK_WIDGET (self)); } - diff --git a/src/sysprof/sysprof-marks-section.ui b/src/sysprof/sysprof-marks-section.ui index 692fe718..76f11645 100644 --- a/src/sysprof/sysprof-marks-section.ui +++ b/src/sysprof/sysprof-marks-section.ui @@ -5,6 +5,51 @@ vertical + + + + SysprofMarksSection + + + + 6 + 6 + 20 + + + Marks + + + SysprofMarksSection + + + + + + + + SysprofMarksSection + + + + + + + + + + + + + + + + + + + + + true From 0d5d30dd60299943850fb20423cba1292c0497aa Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 12 Jul 2023 18:05:18 -0700 Subject: [PATCH 0846/1030] sysprof: connect session filter to traceables --- src/sysprof/sysprof-samples-section.ui | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/sysprof/sysprof-samples-section.ui b/src/sysprof/sysprof-samples-section.ui index 2f9e4c63..a844ecb3 100644 --- a/src/sysprof/sysprof-samples-section.ui +++ b/src/sysprof/sysprof-samples-section.ui @@ -72,14 +72,22 @@ SysprofSamplesSection - - - - - SysprofSamplesSection - - - + + + + + SysprofSamplesSection + + + + + + SysprofSamplesSection + + + + + From 7c02b0f62c3f893a46f691adf455fdba8eb76798 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 13 Jul 2023 08:43:49 -0700 Subject: [PATCH 0847/1030] libsysprof-analyze: include CPU core-id --- .../sysprof-cpu-info-private.h | 2 ++ src/libsysprof-analyze/sysprof-cpu-info.c | 36 +++++++++++++++++++ src/libsysprof-analyze/sysprof-cpu-info.h | 2 ++ src/libsysprof-analyze/sysprof-document.c | 8 +++++ src/sysprof/sysprof-cpu-info-dialog.ui | 35 ++++++++++++++++++ 5 files changed, 83 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-cpu-info-private.h b/src/libsysprof-analyze/sysprof-cpu-info-private.h index ef785717..1345231e 100644 --- a/src/libsysprof-analyze/sysprof-cpu-info-private.h +++ b/src/libsysprof-analyze/sysprof-cpu-info-private.h @@ -24,6 +24,8 @@ G_BEGIN_DECLS +void _sysprof_cpu_info_set_core_id (SysprofCpuInfo *self, + guint core_id); void _sysprof_cpu_info_set_model_name (SysprofCpuInfo *self, const char *model_name); diff --git a/src/libsysprof-analyze/sysprof-cpu-info.c b/src/libsysprof-analyze/sysprof-cpu-info.c index b24b43b2..909a8a90 100644 --- a/src/libsysprof-analyze/sysprof-cpu-info.c +++ b/src/libsysprof-analyze/sysprof-cpu-info.c @@ -27,11 +27,13 @@ struct _SysprofCpuInfo GObject parent_instance; char *model_name; guint id; + guint core_id; }; enum { PROP_0, PROP_ID, + PROP_CORE_ID, PROP_MODEL_NAME, N_PROPS }; @@ -64,6 +66,10 @@ sysprof_cpu_info_get_property (GObject *object, g_value_set_uint (value, self->id); break; + case PROP_CORE_ID: + g_value_set_uint (value, self->core_id); + break; + case PROP_MODEL_NAME: g_value_set_string (value, self->model_name); break; @@ -87,6 +93,10 @@ sysprof_cpu_info_set_property (GObject *object, self->id = g_value_get_uint (value); break; + case PROP_CORE_ID: + self->core_id = g_value_get_uint (value); + break; + case PROP_MODEL_NAME: self->model_name = g_value_dup_string (value); break; @@ -110,6 +120,11 @@ sysprof_cpu_info_class_init (SysprofCpuInfoClass *klass) 0, G_MAXUINT, 0, (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + properties[PROP_CORE_ID] = + g_param_spec_uint ("core-id", NULL, NULL, + 0, G_MAXUINT, 0, + (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + properties[PROP_MODEL_NAME] = g_param_spec_string ("model-name", NULL, NULL, NULL, @@ -148,3 +163,24 @@ _sysprof_cpu_info_set_model_name (SysprofCpuInfo *self, if (g_set_str (&self->model_name, model_name)) g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODEL_NAME]); } + +guint +sysprof_cpu_info_get_core_id (SysprofCpuInfo *self) +{ + g_return_val_if_fail (SYSPROF_IS_CPU_INFO (self), 0); + + return self->core_id; +} + +void +_sysprof_cpu_info_set_core_id (SysprofCpuInfo *self, + guint core_id) +{ + g_return_if_fail (SYSPROF_IS_CPU_INFO (self)); + + if (self->core_id != core_id) + { + self->core_id = core_id; + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_CORE_ID]); + } +} diff --git a/src/libsysprof-analyze/sysprof-cpu-info.h b/src/libsysprof-analyze/sysprof-cpu-info.h index b61babe4..2e66f5c9 100644 --- a/src/libsysprof-analyze/sysprof-cpu-info.h +++ b/src/libsysprof-analyze/sysprof-cpu-info.h @@ -34,6 +34,8 @@ G_DECLARE_FINAL_TYPE (SysprofCpuInfo, sysprof_cpu_info, SYSPROF, CPU_INFO, GObje SYSPROF_AVAILABLE_IN_ALL guint sysprof_cpu_info_get_id (SysprofCpuInfo *self); SYSPROF_AVAILABLE_IN_ALL +guint sysprof_cpu_info_get_core_id (SysprofCpuInfo *self); +SYSPROF_AVAILABLE_IN_ALL const char *sysprof_cpu_info_get_model_name (SysprofCpuInfo *self); G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index fb7a8324..59695d13 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -1046,6 +1046,14 @@ sysprof_document_load_cpu (SysprofDocument *self) NULL); } + if (g_str_has_prefix (line, "core id\t\t: ")) + { + gint64 core_id = g_ascii_strtoll (line+strlen("core id\t\t: "), NULL, 10); + + if (core_id > 0) + _sysprof_cpu_info_set_core_id (cpu_info, core_id); + } + if (g_str_has_prefix (line, "model name\t: ")) { const gsize model_name_len = strlen ("model name\t: "); diff --git a/src/sysprof/sysprof-cpu-info-dialog.ui b/src/sysprof/sysprof-cpu-info-dialog.ui index decb5530..71a50d5e 100644 --- a/src/sysprof/sysprof-cpu-info-dialog.ui +++ b/src/sysprof/sysprof-cpu-info-dialog.ui @@ -69,6 +69,41 @@ +]]> + + + + + + + + Core + + + + + + + + + + + + + ]]> From f39723c2e68e1a21b269e64943ec06ec5a1041f2 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 13 Jul 2023 11:04:01 -0700 Subject: [PATCH 0848/1030] libsysprof-analyze: add equal func for timespan --- src/libsysprof-analyze/sysprof-time-span.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-time-span.h b/src/libsysprof-analyze/sysprof-time-span.h index d76b73e7..a85595a8 100644 --- a/src/libsysprof-analyze/sysprof-time-span.h +++ b/src/libsysprof-analyze/sysprof-time-span.h @@ -130,4 +130,17 @@ sysprof_time_span_to_string (const SysprofTimeSpan *span) return g_strdup_printf ("%s (%s)", begin, end); } +static inline gboolean +sysprof_time_span_equal (const SysprofTimeSpan *a, + const SysprofTimeSpan *b) +{ + if (a == b) + return TRUE; + + if (a == NULL || b == NULL) + return FALSE; + + return memcmp (a, b, sizeof *a) == 0; +} + G_END_DECLS From b9f7ecd945cfb038cd0c785efd66d3c307659e2f Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 13 Jul 2023 11:05:22 -0700 Subject: [PATCH 0849/1030] sysprof: add SysprofTimeFilterModel This is a model that expects the set to be in sorted order by time, and requires that the items are a SysprofDocumentFrame. If that holds true, it can binary search to find the correct frames based on a requested time-span that is inclusive only of matching frames. This is useful to dive down into data based on either the visible or selected time span of a session without having to expensive GtkFilter operations (which would look at every object in the set). --- src/sysprof/meson.build | 1 + src/sysprof/sysprof-time-filter-model.c | 412 ++++++++++++++++++++++++ src/sysprof/sysprof-time-filter-model.h | 42 +++ 3 files changed, 455 insertions(+) create mode 100644 src/sysprof/sysprof-time-filter-model.c create mode 100644 src/sysprof/sysprof-time-filter-model.h diff --git a/src/sysprof/meson.build b/src/sysprof/meson.build index d4e30ae1..c63721f1 100644 --- a/src/sysprof/meson.build +++ b/src/sysprof/meson.build @@ -16,6 +16,7 @@ sysprof_sources = [ 'sysprof-sidebar.c', 'sysprof-single-model.c', 'sysprof-time-scrubber.c', + 'sysprof-time-filter-model.c', 'sysprof-traceables-utility.c', 'sysprof-window.c', ] diff --git a/src/sysprof/sysprof-time-filter-model.c b/src/sysprof/sysprof-time-filter-model.c new file mode 100644 index 00000000..3ae91911 --- /dev/null +++ b/src/sysprof/sysprof-time-filter-model.c @@ -0,0 +1,412 @@ +/* sysprof-time-filter-model.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include + +#include "sysprof-time-filter-model.h" + +struct _SysprofTimeFilterModel +{ + GObject parent_instance; + GtkSliceListModel *slice; + SysprofTimeSpan time_span; +}; + +enum { + PROP_0, + PROP_MODEL, + PROP_TIME_SPAN, + N_PROPS +}; + +static GType +sysprof_time_filter_model_get_item_type (GListModel *model) +{ + return g_list_model_get_item_type (G_LIST_MODEL (SYSPROF_TIME_FILTER_MODEL (model)->slice)); +} + +static guint +sysprof_time_filter_model_get_n_items (GListModel *model) +{ + return g_list_model_get_n_items (G_LIST_MODEL (SYSPROF_TIME_FILTER_MODEL (model)->slice)); +} + +static gpointer +sysprof_time_filter_model_get_item (GListModel *model, + guint position) +{ + return g_list_model_get_item (G_LIST_MODEL (SYSPROF_TIME_FILTER_MODEL (model)->slice), position); +} + +static void +list_model_iface_init (GListModelInterface *iface) +{ + iface->get_item_type = sysprof_time_filter_model_get_item_type; + iface->get_n_items = sysprof_time_filter_model_get_n_items; + iface->get_item = sysprof_time_filter_model_get_item; +} + +G_DEFINE_FINAL_TYPE_WITH_CODE (SysprofTimeFilterModel, sysprof_time_filter_model, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init)) + +static GParamSpec *properties [N_PROPS]; + +static void +sysprof_time_filter_model_finalize (GObject *object) +{ + SysprofTimeFilterModel *self = (SysprofTimeFilterModel *)object; + + g_clear_object (&self->slice); + + G_OBJECT_CLASS (sysprof_time_filter_model_parent_class)->finalize (object); +} + +static void +sysprof_time_filter_model_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofTimeFilterModel *self = SYSPROF_TIME_FILTER_MODEL (object); + + switch (prop_id) + { + case PROP_MODEL: + g_value_set_object (value, sysprof_time_filter_model_get_model (self)); + break; + + case PROP_TIME_SPAN: + g_value_set_boxed (value, sysprof_time_filter_model_get_time_span (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_time_filter_model_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SysprofTimeFilterModel *self = SYSPROF_TIME_FILTER_MODEL (object); + + switch (prop_id) + { + case PROP_MODEL: + sysprof_time_filter_model_set_model (self, g_value_get_object (value)); + break; + + case PROP_TIME_SPAN: + sysprof_time_filter_model_set_time_span (self, g_value_get_boxed (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_time_filter_model_class_init (SysprofTimeFilterModelClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = sysprof_time_filter_model_finalize; + object_class->get_property = sysprof_time_filter_model_get_property; + object_class->set_property = sysprof_time_filter_model_set_property; + + properties[PROP_MODEL] = + g_param_spec_object ("model", NULL, NULL, + G_TYPE_LIST_MODEL, + (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); + + properties[PROP_TIME_SPAN] = + g_param_spec_boxed ("time-span", NULL, NULL, + SYSPROF_TYPE_TIME_SPAN, + (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); +} + +static void +sysprof_time_filter_model_init (SysprofTimeFilterModel *self) +{ + self->time_span.begin_nsec = G_MININT64; + self->time_span.end_nsec = G_MAXINT64; + + self->slice = gtk_slice_list_model_new (NULL, 0, GTK_INVALID_LIST_POSITION); + g_signal_connect_object (self->slice, + "items-changed", + G_CALLBACK (g_list_model_items_changed), + self, + G_CONNECT_SWAPPED); +} + +static inline void +uint_order (guint *a, + guint *b) +{ + if (*a > *b) + { + guint what_was_a = *a; + *a = *b; + *b = what_was_a; + } +} + +static guint +binary_search_lte (GListModel *model, + gint64 value) +{ + g_autoptr(SysprofDocumentFrame) first = NULL; + g_autoptr(SysprofDocumentFrame) last = NULL; + guint lo; + guint hi; + guint n_items; + + g_assert (G_IS_LIST_MODEL (model)); + g_assert (g_list_model_get_n_items (model) > 0); + + n_items = g_list_model_get_n_items (model); + first = g_list_model_get_item (model, 0); + last = g_list_model_get_item (model, n_items - 1); + + if (value < sysprof_document_frame_get_time (first)) + return GTK_INVALID_LIST_POSITION; + else if (value == sysprof_document_frame_get_time (first)) + return 0; + else if (value >= sysprof_document_frame_get_time (last)) + return n_items - 1; + + g_clear_object (&first); + g_clear_object (&last); + + lo = 0; + hi = n_items - 1; + + while (lo <= hi) + { + guint mid = lo + (hi - lo) / 2; + g_autoptr(SysprofDocumentFrame) frame = g_list_model_get_item (model, mid); + + if (value < sysprof_document_frame_get_time (frame)) + hi = mid - 1; + else if (value > sysprof_document_frame_get_time (frame)) + lo = mid + 1; + else + return mid; + } + + uint_order (&lo, &hi); + + first = g_list_model_get_item (model, lo); + last = g_list_model_get_item (model, hi); + + if (value < sysprof_document_frame_get_time (first)) + return lo - 1; + else if (value > sysprof_document_frame_get_time (first)) + return lo; + else if (value < sysprof_document_frame_get_time (last)) + return hi; + + return GTK_INVALID_LIST_POSITION; +} + +static guint +binary_search_gte (GListModel *model, + gint64 value) +{ + g_autoptr(SysprofDocumentFrame) first = NULL; + g_autoptr(SysprofDocumentFrame) last = NULL; + guint lo; + guint hi; + guint n_items; + + g_assert (G_IS_LIST_MODEL (model)); + g_assert (g_list_model_get_n_items (model) > 0); + + n_items = g_list_model_get_n_items (model); + first = g_list_model_get_item (model, 0); + last = g_list_model_get_item (model, n_items - 1); + + if (value <= sysprof_document_frame_get_time (first)) + return 0; + else if (value == sysprof_document_frame_get_time (last)) + return n_items - 1; + else if (value > sysprof_document_frame_get_time (last)) + return GTK_INVALID_LIST_POSITION; + + g_clear_object (&first); + g_clear_object (&last); + + lo = 0; + hi = n_items - 1; + + while (lo <= hi) + { + guint mid = lo + (hi - lo) / 2; + g_autoptr(SysprofDocumentFrame) frame = g_list_model_get_item (model, mid); + + if (value < sysprof_document_frame_get_time (frame)) + hi = mid - 1; + else if (value > sysprof_document_frame_get_time (frame)) + lo = mid + 1; + else + return mid; + } + + uint_order (&lo, &hi); + + first = g_list_model_get_item (model, lo); + last = g_list_model_get_item (model, hi); + + if (value > sysprof_document_frame_get_time (first)) + return lo; + + if (value < sysprof_document_frame_get_time (last)) + return hi - 1; + + return GTK_INVALID_LIST_POSITION; +} + +static void +calculate_bounds (GListModel *model, + const SysprofTimeSpan *time_span, + guint *offset, + guint *size) +{ + guint begin; + guint end; + + g_assert (!model || G_IS_LIST_MODEL (model)); + g_assert (time_span != NULL); + g_assert (offset != NULL); + g_assert (size != NULL); + + *offset = 0; + *size = GTK_INVALID_LIST_POSITION-1; + + if (model == NULL || g_list_model_get_n_items (model) == 0) + return; + + if (time_span->begin_nsec == G_MININT64 && time_span->end_nsec == G_MAXINT64) + return; + + begin = binary_search_gte (model, time_span->begin_nsec); + end = binary_search_lte (model, time_span->end_nsec); + + if (begin > end || begin == GTK_INVALID_LIST_POSITION) + return; + + *offset = begin; + + if (end != GTK_INVALID_LIST_POSITION) + *size = end - begin + 1; +} + +static void +sysprof_time_filter_model_update (SysprofTimeFilterModel *self) +{ + GListModel *model; + guint offset; + guint size; + + g_assert (SYSPROF_IS_TIME_FILTER_MODEL (self)); + + model = sysprof_time_filter_model_get_model (self); + + calculate_bounds (model, &self->time_span, &offset, &size); + + gtk_slice_list_model_set_offset (self->slice, offset); + gtk_slice_list_model_set_size (self->slice, size); +} + +SysprofTimeFilterModel * +sysprof_time_filter_model_new (GListModel *model, + const SysprofTimeSpan *time_span) +{ + SysprofTimeFilterModel *self; + + g_return_val_if_fail (!model || G_IS_LIST_MODEL (model), NULL); + + self = g_object_new (SYSPROF_TYPE_TIME_FILTER_MODEL, + "model", model, + "time-span", time_span, + NULL); + + g_clear_object (&model); + + return self; +} + +GListModel * +sysprof_time_filter_model_get_model (SysprofTimeFilterModel *self) +{ + g_return_val_if_fail (SYSPROF_IS_TIME_FILTER_MODEL (self), NULL); + + return gtk_slice_list_model_get_model (self->slice); +} + +void +sysprof_time_filter_model_set_model (SysprofTimeFilterModel *self, + GListModel *model) +{ + g_return_if_fail (SYSPROF_IS_TIME_FILTER_MODEL (self)); + g_return_if_fail (!model || G_IS_LIST_MODEL (model)); + g_return_if_fail (!model || g_type_is_a (g_list_model_get_item_type (model), + SYSPROF_TYPE_DOCUMENT_FRAME)); + + if (model == sysprof_time_filter_model_get_model (self)) + return; + + gtk_slice_list_model_set_model (self->slice, model); + sysprof_time_filter_model_update (self); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODEL]); +} + +const SysprofTimeSpan * +sysprof_time_filter_model_get_time_span (SysprofTimeFilterModel *self) +{ + g_return_val_if_fail (SYSPROF_IS_TIME_FILTER_MODEL (self), NULL); + + return &self->time_span; +} + +void +sysprof_time_filter_model_set_time_span (SysprofTimeFilterModel *self, + const SysprofTimeSpan *time_span) +{ + SysprofTimeSpan empty = {G_MININT64, G_MAXINT64}; + + g_return_if_fail (SYSPROF_IS_TIME_FILTER_MODEL (self)); + + if (time_span == NULL) + time_span = ∅ + + if (!sysprof_time_span_equal (&self->time_span, time_span)) + { + self->time_span = *time_span; + sysprof_time_filter_model_update (self); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_TIME_SPAN]); + } +} diff --git a/src/sysprof/sysprof-time-filter-model.h b/src/sysprof/sysprof-time-filter-model.h new file mode 100644 index 00000000..7c54dd20 --- /dev/null +++ b/src/sysprof/sysprof-time-filter-model.h @@ -0,0 +1,42 @@ +/* sysprof-time-filter-model.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +#include + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_TIME_FILTER_MODEL (sysprof_time_filter_model_get_type()) + +G_DECLARE_FINAL_TYPE (SysprofTimeFilterModel, sysprof_time_filter_model, SYSPROF, TIME_FILTER_MODEL, GObject) + +SysprofTimeFilterModel *sysprof_time_filter_model_new (GListModel *model, + const SysprofTimeSpan *time_span); +GListModel *sysprof_time_filter_model_get_model (SysprofTimeFilterModel *self); +void sysprof_time_filter_model_set_model (SysprofTimeFilterModel *self, + GListModel *model); +const SysprofTimeSpan *sysprof_time_filter_model_get_time_span (SysprofTimeFilterModel *self); +void sysprof_time_filter_model_set_time_span (SysprofTimeFilterModel *self, + const SysprofTimeSpan *time_span); + +G_END_DECLS From 72cf6ff3a54be6b1b0214f50d0ed95bf87d5f7f1 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 13 Jul 2023 11:06:06 -0700 Subject: [PATCH 0850/1030] sysprof: filter sample graph by visible time range This allows it to avoid a bunch of work when you've dived down into a section of visible time, as fewer nodes need to be looked at. --- src/sysprof/sysprof-samples-section.c | 2 ++ src/sysprof/sysprof-samples-section.ui | 23 ++++++++++++++++------- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/sysprof/sysprof-samples-section.c b/src/sysprof/sysprof-samples-section.c index 614423fb..10160797 100644 --- a/src/sysprof/sysprof-samples-section.c +++ b/src/sysprof/sysprof-samples-section.c @@ -24,6 +24,7 @@ #include "sysprof-samples-section.h" #include "sysprof-traceables-utility.h" +#include "sysprof-time-filter-model.h" #include "sysprof-time-scrubber.h" struct _SysprofSamplesSection @@ -59,6 +60,7 @@ sysprof_samples_section_class_init (SysprofSamplesSectionClass *klass) g_type_ensure (SYSPROF_TYPE_CHART); g_type_ensure (SYSPROF_TYPE_XY_SERIES); g_type_ensure (SYSPROF_TYPE_COLUMN_LAYER); + g_type_ensure (SYSPROF_TYPE_TIME_FILTER_MODEL); g_type_ensure (SYSPROF_TYPE_TIME_SCRUBBER); g_type_ensure (SYSPROF_TYPE_TRACEABLES_UTILITY); g_type_ensure (SYSPROF_TYPE_VALUE_AXIS); diff --git a/src/sysprof/sysprof-samples-section.ui b/src/sysprof/sysprof-samples-section.ui index a844ecb3..28342975 100644 --- a/src/sysprof/sysprof-samples-section.ui +++ b/src/sysprof/sysprof-samples-section.ui @@ -29,13 +29,22 @@ - - - - SysprofSamplesSection - - - + + + + + SysprofSamplesSection + + + + + + SysprofSamplesSection + + + + + From f147cc6be03c247d4b874d4c81791496f309d300 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 13 Jul 2023 11:14:30 -0700 Subject: [PATCH 0851/1030] libsysprof-gtk: invalidate callgraph when traceables change If we get an items-changed event for the callgraph traceables, we need to regenerate the callgraph. Queue a reload under such a situation. --- .../sysprof-callgraph-view-private.h | 1 + src/libsysprof-gtk/sysprof-callgraph-view.c | 37 +++++++++++++++---- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/src/libsysprof-gtk/sysprof-callgraph-view-private.h b/src/libsysprof-gtk/sysprof-callgraph-view-private.h index 4efd72b6..07f84992 100644 --- a/src/libsysprof-gtk/sysprof-callgraph-view-private.h +++ b/src/libsysprof-gtk/sysprof-callgraph-view-private.h @@ -34,6 +34,7 @@ struct _SysprofCallgraphView SysprofDocument *document; SysprofCallgraph *callgraph; + GSignalGroup *traceables_signals; GListModel *traceables; GListModel *utility_traceables; diff --git a/src/libsysprof-gtk/sysprof-callgraph-view.c b/src/libsysprof-gtk/sysprof-callgraph-view.c index d467ee18..56b437e7 100644 --- a/src/libsysprof-gtk/sysprof-callgraph-view.c +++ b/src/libsysprof-gtk/sysprof-callgraph-view.c @@ -38,13 +38,14 @@ enum { N_PROPS }; -static void buildable_iface_init (GtkBuildableIface *iface); -static GListModel *sysprof_callgraph_view_create_model_func (gpointer item, - gpointer user_data); -static void descendants_selection_changed_cb (SysprofCallgraphView *self, - guint position, - guint n_items, - GtkSingleSelection *single); +static void buildable_iface_init (GtkBuildableIface *iface); +static GListModel *sysprof_callgraph_view_create_model_func (gpointer item, + gpointer user_data); +static void descendants_selection_changed_cb (SysprofCallgraphView *self, + guint position, + guint n_items, + GtkSingleSelection *single); +static void sysprof_callgraph_view_queue_reload (SysprofCallgraphView *self); G_DEFINE_ABSTRACT_TYPE_WITH_CODE (SysprofCallgraphView, sysprof_callgraph_view, GTK_TYPE_WIDGET, G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, buildable_iface_init)) @@ -302,6 +303,14 @@ sysprof_callgraph_view_dispose (GObject *object) { SysprofCallgraphView *self = (SysprofCallgraphView *)object; + if (self->traceables_signals) + { + g_signal_group_set_target (self->traceables_signals, NULL); + g_clear_object (&self->traceables_signals); + } + + gtk_widget_dispose_template (GTK_WIDGET (self), SYSPROF_TYPE_CALLGRAPH_VIEW); + g_clear_handle_id (&self->reload_source, g_source_remove); g_clear_pointer (&self->paned, gtk_widget_unparent); @@ -450,6 +459,18 @@ sysprof_callgraph_view_class_init (SysprofCallgraphViewClass *klass) static void sysprof_callgraph_view_init (SysprofCallgraphView *self) { + self->traceables_signals = g_signal_group_new (G_TYPE_LIST_MODEL); + g_signal_connect_object (self->traceables_signals, + "bind", + G_CALLBACK (sysprof_callgraph_view_queue_reload), + self, + G_CONNECT_SWAPPED); + g_signal_group_connect_object (self->traceables_signals, + "items-changed", + G_CALLBACK (sysprof_callgraph_view_queue_reload), + self, + G_CONNECT_SWAPPED); + gtk_widget_init_template (GTK_WIDGET (self)); } @@ -650,7 +671,7 @@ sysprof_callgraph_view_set_traceables (SysprofCallgraphView *self, if (g_set_object (&self->traceables, traceables)) { - sysprof_callgraph_view_queue_reload (self); + g_signal_group_set_target (self->traceables_signals, traceables); g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_TRACEABLES]); } } From 730f8f4d66044099ff28e6922b2d571a7439c87b Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 13 Jul 2023 11:16:14 -0700 Subject: [PATCH 0852/1030] sysprof: filter the callgraph by the selected time span This ensures the callgraph code has fewer items to look at. The first level of filtering is done by the SysprofDocument bitset index for items matching the type (via SysprofDocumentBitsetIndex). Then the new SysprofTimeFilterModel filters the frames by time-span by binary searching for the first and last indexes which match that time span, avoiding the pathological case of linear search for the spans (which would inflate a GObject for every element). --- src/sysprof/sysprof-samples-section.ui | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/sysprof/sysprof-samples-section.ui b/src/sysprof/sysprof-samples-section.ui index 28342975..bf031860 100644 --- a/src/sysprof/sysprof-samples-section.ui +++ b/src/sysprof/sysprof-samples-section.ui @@ -88,13 +88,22 @@ SysprofSamplesSection - - - - SysprofSamplesSection - - - + + + + + SysprofSamplesSection + + + + + + SysprofSamplesSection + + + + + From fb1c65af5cf531a1dc35369859ef575a21b5b104 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 13 Jul 2023 12:58:57 -0700 Subject: [PATCH 0853/1030] libsysprof-gtk: describe log messages --- src/libsysprof-gtk/sysprof-session.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/libsysprof-gtk/sysprof-session.c b/src/libsysprof-gtk/sysprof-session.c index b690fb54..8788e556 100644 --- a/src/libsysprof-gtk/sysprof-session.c +++ b/src/libsysprof-gtk/sysprof-session.c @@ -506,5 +506,23 @@ _sysprof_session_describe (SysprofSession *self, return g_string_free (str, FALSE); } + if (SYSPROF_IS_DOCUMENT_LOG (item)) + { + SysprofDocumentLog *log = item; + const SysprofTimeSpan *begin = sysprof_document_get_time_span (self->document); + GString *str = g_string_new (NULL); + const char *domain = sysprof_document_log_get_domain (log); + const char *message = sysprof_document_log_get_message (log); + gint64 t = sysprof_document_frame_get_time (item); + SysprofTimeSpan span = { t, t }; + + span = sysprof_time_span_relative_to (span, begin->begin_nsec); + + append_time_string (str, &span); + g_string_append_printf (str, ": %s: %s", domain, message); + + return g_string_free (str, FALSE); + } + return NULL; } From df5c1b702070e34e93e48f506998e63cc0d693bd Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 13 Jul 2023 13:00:13 -0700 Subject: [PATCH 0854/1030] sysprof: add time scrubber to logs section --- src/sysprof/sysprof-logs-section.ui | 52 +++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/src/sysprof/sysprof-logs-section.ui b/src/sysprof/sysprof-logs-section.ui index e78929a0..e797dfc9 100644 --- a/src/sysprof/sysprof-logs-section.ui +++ b/src/sysprof/sysprof-logs-section.ui @@ -5,6 +5,58 @@ vertical + + + + SysprofLogsSection + + + + 32 + + + Logs + + + SysprofLogsSection + + + + + + + + + SysprofLogsSection + + + + + + SysprofLogsSection + + + + + + + + + + + + + + + + + + + + + + + true From ace57165623cbd07c39613e4734eee5929aa9fc2 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 13 Jul 2023 13:14:07 -0700 Subject: [PATCH 0855/1030] sysprof: filter logs by selected time --- src/sysprof/sysprof-logs-section.c | 2 ++ src/sysprof/sysprof-logs-section.ui | 23 ++++++++++++++++------- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/sysprof/sysprof-logs-section.c b/src/sysprof/sysprof-logs-section.c index 63fae182..c10c0372 100644 --- a/src/sysprof/sysprof-logs-section.c +++ b/src/sysprof/sysprof-logs-section.c @@ -86,6 +86,8 @@ sysprof_logs_section_class_init (SysprofLogsSectionClass *klass) g_type_ensure (SYSPROF_TYPE_DOCUMENT_LOG); g_type_ensure (SYSPROF_TYPE_TIME_LABEL); + g_type_ensure (SYSPROF_TYPE_TIME_SERIES); + g_type_ensure (SYSPROF_TYPE_TIME_SPAN_LAYER); } static void diff --git a/src/sysprof/sysprof-logs-section.ui b/src/sysprof/sysprof-logs-section.ui index e797dfc9..e73d515f 100644 --- a/src/sysprof/sysprof-logs-section.ui +++ b/src/sysprof/sysprof-logs-section.ui @@ -73,13 +73,22 @@ column_view - - - - SysprofLogsSection - - - + + + + + SysprofLogsSection + + + + + + SysprofLogsSection + + + + + From 0bc18816d8ecbb49f794f2ec6761bef7cd708649 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 13 Jul 2023 14:36:40 -0700 Subject: [PATCH 0856/1030] libsysprof-analyze: remove 32-bit time_offset optimization This simply isn't worth the memory saves for the loss of precision. Just use a 64-bit and store the actual time offset. --- src/libsysprof-analyze/sysprof-document-frame-private.h | 2 +- src/libsysprof-analyze/sysprof-document-frame.c | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-document-frame-private.h b/src/libsysprof-analyze/sysprof-document-frame-private.h index 82ad8e2e..e10aaee2 100644 --- a/src/libsysprof-analyze/sysprof-document-frame-private.h +++ b/src/libsysprof-analyze/sysprof-document-frame-private.h @@ -31,10 +31,10 @@ struct _SysprofDocumentFrame GObject parent; GMappedFile *mapped_file; const SysprofCaptureFrame *frame; + gint64 time_offset; guint32 frame_len : 16; guint32 needs_swap : 1; guint32 padding : 15; - guint32 time_offset; }; struct _SysprofDocumentFrameClass diff --git a/src/libsysprof-analyze/sysprof-document-frame.c b/src/libsysprof-analyze/sysprof-document-frame.c index 4221de6d..014bfd51 100644 --- a/src/libsysprof-analyze/sysprof-document-frame.c +++ b/src/libsysprof-analyze/sysprof-document-frame.c @@ -147,7 +147,6 @@ _sysprof_document_frame_new (GMappedFile *mapped_file, gint64 end_time) { SysprofDocumentFrame *self; - gint64 time_offset; GType gtype; switch (frame->type) @@ -220,10 +219,7 @@ _sysprof_document_frame_new (GMappedFile *mapped_file, self->frame_len = frame_len; self->needs_swap = !!needs_swap; - time_offset = CLAMP (sysprof_document_frame_get_time (self) - begin_time, 0, G_MAXINT64); - - /* loose precision here after about 71 minutes */ - self->time_offset = (guint)time_offset; + self->time_offset = CLAMP (sysprof_document_frame_get_time (self) - begin_time, 0, G_MAXINT64); return self; } From 30177af92c3a5dea483d291b5f6dfd4c74b87101 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 13 Jul 2023 14:40:42 -0700 Subject: [PATCH 0857/1030] sysprof: fix gte boundary for binary search to slice --- src/sysprof/sysprof-time-filter-model.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/sysprof/sysprof-time-filter-model.c b/src/sysprof/sysprof-time-filter-model.c index 3ae91911..959ee133 100644 --- a/src/sysprof/sysprof-time-filter-model.c +++ b/src/sysprof/sysprof-time-filter-model.c @@ -281,9 +281,8 @@ binary_search_gte (GListModel *model, last = g_list_model_get_item (model, hi); if (value > sysprof_document_frame_get_time (first)) - return lo; - - if (value < sysprof_document_frame_get_time (last)) + return lo + 1; + else if (value < sysprof_document_frame_get_time (last)) return hi - 1; return GTK_INVALID_LIST_POSITION; From e55d74d80c6554bf113c400da85b770a9ecf0dc8 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 13 Jul 2023 14:55:00 -0700 Subject: [PATCH 0858/1030] libsysprof-gtk: give session a copy of document time This way we don't have to constantly route through the document for it to determine relative axis information. --- src/libsysprof-gtk/sysprof-session.c | 22 ++++++++++++++++++++-- src/libsysprof-gtk/sysprof-session.h | 2 ++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/libsysprof-gtk/sysprof-session.c b/src/libsysprof-gtk/sysprof-session.c index 8788e556..ef5a1b88 100644 --- a/src/libsysprof-gtk/sysprof-session.c +++ b/src/libsysprof-gtk/sysprof-session.c @@ -38,6 +38,7 @@ struct _SysprofSession SysprofAxis *visible_time_axis; SysprofAxis *selected_time_axis; + SysprofTimeSpan document_time; SysprofTimeSpan selected_time; SysprofTimeSpan visible_time; @@ -48,6 +49,7 @@ struct _SysprofSession enum { PROP_0, PROP_DOCUMENT, + PROP_DOCUMENT_TIME, PROP_FILTER, PROP_HIDE_SYSTEM_LIBRARIES, PROP_INCLUDE_THREADS, @@ -93,8 +95,7 @@ sysprof_session_set_document (SysprofSession *self, /* Select/show the entire document time span */ time_span = sysprof_document_get_time_span (document); - memcpy (&self->visible_time, time_span, sizeof *time_span); - memcpy (&self->selected_time, time_span, sizeof *time_span); + self->selected_time = self->visible_time = self->document_time = *time_span; sysprof_session_update_axis (self); /* Discover tracks to show from the document */ @@ -129,6 +130,10 @@ sysprof_session_get_property (GObject *object, g_value_set_object (value, sysprof_session_get_document (self)); break; + case PROP_DOCUMENT_TIME: + g_value_set_boxed (value, sysprof_session_get_document_time (self)); + break; + case PROP_FILTER: g_value_set_object (value, sysprof_session_get_filter (self)); break; @@ -207,6 +212,11 @@ sysprof_session_class_init (SysprofSessionClass *klass) SYSPROF_TYPE_DOCUMENT, (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + properties [PROP_DOCUMENT_TIME] = + g_param_spec_boxed ("document-time", NULL, NULL, + SYSPROF_TYPE_TIME_SPAN, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + properties [PROP_FILTER] = g_param_spec_object ("filter", NULL, NULL, GTK_TYPE_FILTER, @@ -316,6 +326,14 @@ sysprof_session_get_visible_time (SysprofSession *self) return &self->visible_time; } +const SysprofTimeSpan * +sysprof_session_get_document_time (SysprofSession *self) +{ + g_return_val_if_fail (SYSPROF_IS_SESSION (self), NULL); + + return &self->document_time; +} + void sysprof_session_select_time (SysprofSession *self, const SysprofTimeSpan *time_span) diff --git a/src/libsysprof-gtk/sysprof-session.h b/src/libsysprof-gtk/sysprof-session.h index 358382db..789f0a18 100644 --- a/src/libsysprof-gtk/sysprof-session.h +++ b/src/libsysprof-gtk/sysprof-session.h @@ -39,6 +39,8 @@ SysprofSession *sysprof_session_new (SysprofDocument SYSPROF_AVAILABLE_IN_ALL SysprofDocument *sysprof_session_get_document (SysprofSession *self); SYSPROF_AVAILABLE_IN_ALL +const SysprofTimeSpan *sysprof_session_get_document_time (SysprofSession *self); +SYSPROF_AVAILABLE_IN_ALL GtkFilter *sysprof_session_get_filter (SysprofSession *self); SYSPROF_AVAILABLE_IN_ALL const SysprofTimeSpan *sysprof_session_get_selected_time (SysprofSession *self); From 7ea03a39691b6d753c45b3ee25674a8390a0f09c Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 13 Jul 2023 15:31:43 -0700 Subject: [PATCH 0859/1030] libsysprof-gtk: improve ruler point placement This makes it so we generate better labels and position them at better (more useful) points in time relative to the document start. --- src/libsysprof-gtk/sysprof-time-ruler.c | 81 +++++++++++++++++-------- 1 file changed, 57 insertions(+), 24 deletions(-) diff --git a/src/libsysprof-gtk/sysprof-time-ruler.c b/src/libsysprof-gtk/sysprof-time-ruler.c index c64691c7..19c28bd1 100644 --- a/src/libsysprof-gtk/sysprof-time-ruler.c +++ b/src/libsysprof-gtk/sysprof-time-ruler.c @@ -47,10 +47,12 @@ sysprof_time_ruler_snapshot (GtkWidget *widget, { SysprofTimeRuler *self = (SysprofTimeRuler *)widget; const SysprofTimeSpan *visible_time; + const SysprofTimeSpan *document_time; PangoLayout *layout; gint64 tick_interval; gint64 time_range; - guint n_groups; + gint64 rem; + int last_x = G_MININT; int width; int height; GdkRGBA color; @@ -75,45 +77,72 @@ sysprof_time_ruler_snapshot (GtkWidget *widget, } G_GNUC_BEGIN_IGNORE_DEPRECATIONS + document_time = sysprof_session_get_document_time (self->session); visible_time = sysprof_session_get_visible_time (self->session); - n_groups = MAX (width / GROUP_SIZE, 1); - time_range = visible_time->end_nsec - visible_time->begin_nsec; - tick_interval = time_range / n_groups; + time_range = sysprof_time_span_duration (*visible_time); layout = gtk_widget_create_pango_layout (widget, NULL); - for (gint64 t = visible_time->begin_nsec - (visible_time->begin_nsec % tick_interval); + if (time_range > SYSPROF_NSEC_PER_SEC*60) + tick_interval = SYSPROF_NSEC_PER_SEC*10; + else if (time_range > SYSPROF_NSEC_PER_SEC*30) + tick_interval = SYSPROF_NSEC_PER_SEC*5; + else if (time_range > SYSPROF_NSEC_PER_SEC*15) + tick_interval = SYSPROF_NSEC_PER_SEC*2.5; + else if (time_range > SYSPROF_NSEC_PER_SEC*5) + tick_interval = SYSPROF_NSEC_PER_SEC; + else if (time_range > SYSPROF_NSEC_PER_SEC*2.5) + tick_interval = SYSPROF_NSEC_PER_SEC*.5; + else if (time_range > SYSPROF_NSEC_PER_SEC) + tick_interval = SYSPROF_NSEC_PER_SEC*.25; + else if (time_range > SYSPROF_NSEC_PER_SEC*.5) + tick_interval = SYSPROF_NSEC_PER_SEC*.1; + else if (time_range > SYSPROF_NSEC_PER_SEC*.25) + tick_interval = SYSPROF_NSEC_PER_SEC*.05; + else if (time_range > SYSPROF_NSEC_PER_SEC*.1) + tick_interval = SYSPROF_NSEC_PER_SEC*.02; + else if (time_range > SYSPROF_NSEC_PER_SEC*.05) + tick_interval = SYSPROF_NSEC_PER_SEC*.01; + else + tick_interval = SYSPROF_NSEC_PER_SEC / 10000; + + rem = (visible_time->begin_nsec - document_time->begin_nsec) % tick_interval; + + for (gint64 t = visible_time->begin_nsec + rem; t < visible_time->end_nsec; t += tick_interval) { gint64 o = t - visible_time->begin_nsec; + gint64 r = t - document_time->begin_nsec; double x = (o / (double)time_range) * width; int pw, ph; char str[32]; - if (x < 0) - continue; + if (x >= 0 && x >= last_x) + { + if (r == 0) + g_snprintf (str, sizeof str, "%.3lfs", .0); + else if (r < 1000000) + g_snprintf (str, sizeof str, "%.3lfμs", r/1000.); + else if (r < SYSPROF_NSEC_PER_SEC) + g_snprintf (str, sizeof str, "%.3lfms", r/1000000.); + else + g_snprintf (str, sizeof str, "%.3lfs", r/(double)SYSPROF_NSEC_PER_SEC); - if (o == 0) - g_snprintf (str, sizeof str, "%.3lfs", .0); - else if (o < 1000000) - g_snprintf (str, sizeof str, "%.3lfμs", o/1000.); - else if (o < SYSPROF_NSEC_PER_SEC) - g_snprintf (str, sizeof str, "%.3lfms", o/1000000.); - else - g_snprintf (str, sizeof str, "%.3lfs", o/(double)SYSPROF_NSEC_PER_SEC); + pango_layout_set_text (layout, str, -1); + pango_layout_get_pixel_size (layout, &pw, &ph); - pango_layout_set_text (layout, str, -1); - pango_layout_get_pixel_size (layout, &pw, &ph); - - gtk_snapshot_save (snapshot); - gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (x-pw-6, (height-ph)/2)); - gtk_snapshot_append_layout (snapshot, layout, &color); - gtk_snapshot_restore (snapshot); + gtk_snapshot_save (snapshot); + gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (x-pw-6, (height-ph)/2)); + gtk_snapshot_append_layout (snapshot, layout, &color); + gtk_snapshot_restore (snapshot); + } gtk_snapshot_append_color (snapshot, &line_color, &GRAPHENE_RECT_INIT (x, 0, 1, height)); + + last_x = x + pw; } g_object_unref (layout); @@ -285,9 +314,11 @@ sysprof_time_ruler_get_label_at_point (SysprofTimeRuler *self, double x) { char str[32]; + const SysprofTimeSpan *document_time; const SysprofTimeSpan *visible; gint64 duration; gint64 o; + int width; g_return_val_if_fail (SYSPROF_IS_TIME_RULER (self), NULL); @@ -300,11 +331,13 @@ sysprof_time_ruler_get_label_at_point (SysprofTimeRuler *self, if (self->session == NULL) return NULL; - if (!(visible = sysprof_session_get_visible_time (self->session)) || + if (!(document_time = sysprof_session_get_document_time (self->session)) || + !(visible = sysprof_session_get_visible_time (self->session)) || !(duration = sysprof_time_span_duration (*visible))) return NULL; - o = (x / (double)gtk_widget_get_width (GTK_WIDGET (self))) * duration; + width = gtk_widget_get_width (GTK_WIDGET (self)); + o = visible->begin_nsec + (x / (double)width * duration) - document_time->begin_nsec; if (o == 0) g_snprintf (str, sizeof str, "%.3lfs", .0); From e6a6f76681669ada374a009bc3148bcfc99e1d49 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 13 Jul 2023 15:53:22 -0700 Subject: [PATCH 0860/1030] libsysprof-gtk: more tick placement improvements Make this a bit better when visible_time != document_time. --- src/libsysprof-gtk/sysprof-time-ruler.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libsysprof-gtk/sysprof-time-ruler.c b/src/libsysprof-gtk/sysprof-time-ruler.c index 19c28bd1..22f885f8 100644 --- a/src/libsysprof-gtk/sysprof-time-ruler.c +++ b/src/libsysprof-gtk/sysprof-time-ruler.c @@ -108,7 +108,7 @@ sysprof_time_ruler_snapshot (GtkWidget *widget, rem = (visible_time->begin_nsec - document_time->begin_nsec) % tick_interval; - for (gint64 t = visible_time->begin_nsec + rem; + for (gint64 t = visible_time->begin_nsec - rem + tick_interval; t < visible_time->end_nsec; t += tick_interval) { From fb6685e841b1283262771339c33b425071bedc90 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 13 Jul 2023 15:53:38 -0700 Subject: [PATCH 0861/1030] sysprof: add scrubber for memory section --- src/sysprof/sysprof-memory-section.ui | 76 +++++++++++++++++---------- 1 file changed, 48 insertions(+), 28 deletions(-) diff --git a/src/sysprof/sysprof-memory-section.ui b/src/sysprof/sysprof-memory-section.ui index 5e166a8a..261d7873 100644 --- a/src/sysprof/sysprof-memory-section.ui +++ b/src/sysprof/sysprof-memory-section.ui @@ -6,43 +6,63 @@ vertical - - 32 - - - Stack Traces - - - SysprofMemorySection - - - - - 0 - 128 - - - - - - - - SysprofMemorySection - + + + SysprofMemorySection + + + + 32 + + + Stack Traces + + + SysprofMemorySection - - + + + 0 + 128 + - - + + + + + + + SysprofMemorySection + + + + + + SysprofMemorySection + + + + + + + + + + + + - + + + + + true From cea61c086890272774f482179fe8b31d0faf357f Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 13 Jul 2023 16:10:02 -0700 Subject: [PATCH 0862/1030] libsysprof-gtk: continue to improve time labels --- src/libsysprof-gtk/sysprof-time-ruler.c | 62 ++++++++++++++++--------- 1 file changed, 41 insertions(+), 21 deletions(-) diff --git a/src/libsysprof-gtk/sysprof-time-ruler.c b/src/libsysprof-gtk/sysprof-time-ruler.c index 22f885f8..63398dc9 100644 --- a/src/libsysprof-gtk/sysprof-time-ruler.c +++ b/src/libsysprof-gtk/sysprof-time-ruler.c @@ -50,7 +50,7 @@ sysprof_time_ruler_snapshot (GtkWidget *widget, const SysprofTimeSpan *document_time; PangoLayout *layout; gint64 tick_interval; - gint64 time_range; + gint64 duration; gint64 rem; int last_x = G_MININT; int width; @@ -79,32 +79,40 @@ sysprof_time_ruler_snapshot (GtkWidget *widget, document_time = sysprof_session_get_document_time (self->session); visible_time = sysprof_session_get_visible_time (self->session); - time_range = sysprof_time_span_duration (*visible_time); + duration = sysprof_time_span_duration (*visible_time); layout = gtk_widget_create_pango_layout (widget, NULL); - if (time_range > SYSPROF_NSEC_PER_SEC*60) + if (duration > SYSPROF_NSEC_PER_SEC*60) tick_interval = SYSPROF_NSEC_PER_SEC*10; - else if (time_range > SYSPROF_NSEC_PER_SEC*30) + else if (duration > SYSPROF_NSEC_PER_SEC*30) tick_interval = SYSPROF_NSEC_PER_SEC*5; - else if (time_range > SYSPROF_NSEC_PER_SEC*15) + else if (duration > SYSPROF_NSEC_PER_SEC*15) tick_interval = SYSPROF_NSEC_PER_SEC*2.5; - else if (time_range > SYSPROF_NSEC_PER_SEC*5) + else if (duration > SYSPROF_NSEC_PER_SEC*5) tick_interval = SYSPROF_NSEC_PER_SEC; - else if (time_range > SYSPROF_NSEC_PER_SEC*2.5) + else if (duration > SYSPROF_NSEC_PER_SEC*2.5) tick_interval = SYSPROF_NSEC_PER_SEC*.5; - else if (time_range > SYSPROF_NSEC_PER_SEC) + else if (duration > SYSPROF_NSEC_PER_SEC) tick_interval = SYSPROF_NSEC_PER_SEC*.25; - else if (time_range > SYSPROF_NSEC_PER_SEC*.5) + else if (duration > SYSPROF_NSEC_PER_SEC*.5) tick_interval = SYSPROF_NSEC_PER_SEC*.1; - else if (time_range > SYSPROF_NSEC_PER_SEC*.25) + else if (duration > SYSPROF_NSEC_PER_SEC*.25) tick_interval = SYSPROF_NSEC_PER_SEC*.05; - else if (time_range > SYSPROF_NSEC_PER_SEC*.1) + else if (duration > SYSPROF_NSEC_PER_SEC*.1) tick_interval = SYSPROF_NSEC_PER_SEC*.02; - else if (time_range > SYSPROF_NSEC_PER_SEC*.05) + else if (duration > SYSPROF_NSEC_PER_SEC*.05) tick_interval = SYSPROF_NSEC_PER_SEC*.01; + else if (duration > SYSPROF_NSEC_PER_SEC*.01) + tick_interval = SYSPROF_NSEC_PER_SEC*.005; + else if (duration > SYSPROF_NSEC_PER_SEC*.005) + tick_interval = SYSPROF_NSEC_PER_SEC*.001; + else if (duration > SYSPROF_NSEC_PER_SEC*.001) + tick_interval = SYSPROF_NSEC_PER_SEC*.0005; + else if (duration > SYSPROF_NSEC_PER_SEC*.0005) + tick_interval = SYSPROF_NSEC_PER_SEC*.00001; else - tick_interval = SYSPROF_NSEC_PER_SEC / 10000; + tick_interval = SYSPROF_NSEC_PER_SEC / 1000000; rem = (visible_time->begin_nsec - document_time->begin_nsec) % tick_interval; @@ -114,11 +122,11 @@ sysprof_time_ruler_snapshot (GtkWidget *widget, { gint64 o = t - visible_time->begin_nsec; gint64 r = t - document_time->begin_nsec; - double x = (o / (double)time_range) * width; + double x = (o / (double)duration) * width; int pw, ph; char str[32]; - if (x >= 0 && x >= last_x) + if (x >= 0 && (x - 6) >= last_x) { if (r == 0) g_snprintf (str, sizeof str, "%.3lfs", .0); @@ -126,6 +134,12 @@ sysprof_time_ruler_snapshot (GtkWidget *widget, g_snprintf (str, sizeof str, "%.3lfμs", r/1000.); else if (r < SYSPROF_NSEC_PER_SEC) g_snprintf (str, sizeof str, "%.3lfms", r/1000000.); + else if (duration < SYSPROF_NSEC_PER_SEC/1000) + g_snprintf (str, sizeof str, "%.6lfs", r/(double)SYSPROF_NSEC_PER_SEC); + else if (duration < SYSPROF_NSEC_PER_SEC/100) + g_snprintf (str, sizeof str, "%.5lfs", r/(double)SYSPROF_NSEC_PER_SEC); + else if (duration < SYSPROF_NSEC_PER_SEC/10) + g_snprintf (str, sizeof str, "%.4lfs", r/(double)SYSPROF_NSEC_PER_SEC); else g_snprintf (str, sizeof str, "%.3lfs", r/(double)SYSPROF_NSEC_PER_SEC); @@ -136,13 +150,13 @@ sysprof_time_ruler_snapshot (GtkWidget *widget, gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (x-pw-6, (height-ph)/2)); gtk_snapshot_append_layout (snapshot, layout, &color); gtk_snapshot_restore (snapshot); + + gtk_snapshot_append_color (snapshot, + &line_color, + &GRAPHENE_RECT_INIT (x, 0, 1, height)); + + last_x = x + pw + 6; } - - gtk_snapshot_append_color (snapshot, - &line_color, - &GRAPHENE_RECT_INIT (x, 0, 1, height)); - - last_x = x + pw; } g_object_unref (layout); @@ -345,6 +359,12 @@ sysprof_time_ruler_get_label_at_point (SysprofTimeRuler *self, g_snprintf (str, sizeof str, "%.3lfμs", o/1000.); else if (o < SYSPROF_NSEC_PER_SEC) g_snprintf (str, sizeof str, "%.3lfms", o/1000000.); + else if (duration < SYSPROF_NSEC_PER_SEC/1000) + g_snprintf (str, sizeof str, "%.6lfs", o/(double)SYSPROF_NSEC_PER_SEC); + else if (duration < SYSPROF_NSEC_PER_SEC/100) + g_snprintf (str, sizeof str, "%.5lfs", o/(double)SYSPROF_NSEC_PER_SEC); + else if (duration < SYSPROF_NSEC_PER_SEC/10) + g_snprintf (str, sizeof str, "%.4lfs", o/(double)SYSPROF_NSEC_PER_SEC); else g_snprintf (str, sizeof str, "%.3lfs", o/(double)SYSPROF_NSEC_PER_SEC); From 882755f267eb9c0dc0bd5d63fd90e0680784de11 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 13 Jul 2023 16:26:54 -0700 Subject: [PATCH 0863/1030] sysprof: improve placement of informative --- src/sysprof/sysprof-time-scrubber.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sysprof/sysprof-time-scrubber.c b/src/sysprof/sysprof-time-scrubber.c index 2e7a2f42..8ee11cc5 100644 --- a/src/sysprof/sysprof-time-scrubber.c +++ b/src/sysprof/sysprof-time-scrubber.c @@ -357,9 +357,9 @@ sysprof_time_scrubber_measure (GtkWidget *widget, static void sysprof_time_scrubber_size_allocate (GtkWidget *widget, - int width, - int height, - int baseline) + int width, + int height, + int baseline) { SysprofTimeScrubber *self = (SysprofTimeScrubber *)widget; graphene_rect_t area; @@ -420,13 +420,13 @@ sysprof_time_scrubber_size_allocate (GtkWidget *widget, if (self->motion_x + min_req.width < gtk_widget_get_width (GTK_WIDGET (self))) gtk_widget_size_allocate (GTK_WIDGET (self->informative), &(GtkAllocation) { - self->motion_x, self->motion_y, + self->motion_x, height - min_req.height, min_req.width, min_req.height }, -1); else gtk_widget_size_allocate (GTK_WIDGET (self->informative), &(GtkAllocation) { - self->motion_x - min_req.width, self->motion_y, + self->motion_x - min_req.width, height - min_req.height, min_req.width, min_req.height }, -1); } From 7bc828035c5134d5ac59994bf499631809d6cff5 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 13 Jul 2023 16:59:55 -0700 Subject: [PATCH 0864/1030] libsysprof-gtk: update time selection on double click Double click an entry in the mark chart to have that set as the time duration for the view. --- src/libsysprof-gtk/sysprof-mark-chart-row.c | 28 ++++++++++++++++++++ src/libsysprof-gtk/sysprof-mark-chart-row.ui | 1 + 2 files changed, 29 insertions(+) diff --git a/src/libsysprof-gtk/sysprof-mark-chart-row.c b/src/libsysprof-gtk/sysprof-mark-chart-row.c index 5be8c59f..216f5368 100644 --- a/src/libsysprof-gtk/sysprof-mark-chart-row.c +++ b/src/libsysprof-gtk/sysprof-mark-chart-row.c @@ -47,6 +47,33 @@ G_DEFINE_FINAL_TYPE (SysprofMarkChartRow, sysprof_mark_chart_row, GTK_TYPE_WIDGE static GParamSpec *properties [N_PROPS]; +static gboolean +sysprof_mark_chart_row_activate_layer_item_cb (SysprofMarkChartRow *self, + SysprofChartLayer *layer, + SysprofDocumentMark *mark, + SysprofChart *chart) +{ + SysprofSession *session; + SysprofTimeSpan time_span; + + g_assert (SYSPROF_IS_MARK_CHART_ROW (self)); + g_assert (SYSPROF_IS_CHART_LAYER (layer)); + g_assert (SYSPROF_IS_DOCUMENT_MARK (mark)); + g_assert (SYSPROF_IS_CHART (chart)); + + if (self->item == NULL || + !(session = sysprof_mark_chart_item_get_session (self->item))) + return FALSE; + + time_span.begin_nsec = sysprof_document_frame_get_time (SYSPROF_DOCUMENT_FRAME (mark)); + time_span.end_nsec = sysprof_document_mark_get_end_time (mark); + + sysprof_session_select_time (session, &time_span); + sysprof_session_zoom_to_selection (session); + + return TRUE; +} + static void sysprof_mark_chart_row_dispose (GObject *object) { @@ -123,6 +150,7 @@ sysprof_mark_chart_row_class_init (SysprofMarkChartRowClass *klass) gtk_widget_class_set_template_from_resource (widget_class, "/libsysprof-gtk/sysprof-mark-chart-row.ui"); gtk_widget_class_bind_template_child (widget_class, SysprofMarkChartRow, chart); gtk_widget_class_bind_template_child (widget_class, SysprofMarkChartRow, layer); + gtk_widget_class_bind_template_callback (widget_class, sysprof_mark_chart_row_activate_layer_item_cb); g_type_ensure (SYSPROF_TYPE_CHART); g_type_ensure (SYSPROF_TYPE_TIME_SERIES_ITEM); diff --git a/src/libsysprof-gtk/sysprof-mark-chart-row.ui b/src/libsysprof-gtk/sysprof-mark-chart-row.ui index 54082bf6..f11bd532 100644 --- a/src/libsysprof-gtk/sysprof-mark-chart-row.ui +++ b/src/libsysprof-gtk/sysprof-mark-chart-row.ui @@ -3,6 +3,7 @@ From 69120dadc8fa93a2d91426bddf5bb40b90cf87f2 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 13 Jul 2023 17:42:03 -0700 Subject: [PATCH 0868/1030] sysprof: add CPU column for traceables list --- src/sysprof/sysprof-traceables-utility.ui | 28 +++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/sysprof/sysprof-traceables-utility.ui b/src/sysprof/sysprof-traceables-utility.ui index 077bd3e5..efe7d4ff 100644 --- a/src/sysprof/sysprof-traceables-utility.ui +++ b/src/sysprof/sysprof-traceables-utility.ui @@ -71,6 +71,34 @@ +]]> + + + + + + + CPU + + + + + + ]]> From 2c8bae5a7f83e79852b4a760abe2628dbd77fd7f Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 13 Jul 2023 17:45:18 -0700 Subject: [PATCH 0869/1030] sysprof: add stack trace view to allocation section --- src/sysprof/sysprof-memory-section.ui | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/sysprof/sysprof-memory-section.ui b/src/sysprof/sysprof-memory-section.ui index 261d7873..c3fa7195 100644 --- a/src/sysprof/sysprof-memory-section.ui +++ b/src/sysprof/sysprof-memory-section.ui @@ -93,6 +93,32 @@ + + + + + + + + Stack Trace + + + + + + + + SysprofMemorySection + + + callgraph_view + + + + + From 1786df875ec4c644be3126b17d24c0d33af196e3 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 13 Jul 2023 17:47:53 -0700 Subject: [PATCH 0870/1030] window: disable sidebar button when not available --- src/sysprof/sysprof-window.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/sysprof/sysprof-window.c b/src/sysprof/sysprof-window.c index c8981ad8..89b5baac 100644 --- a/src/sysprof/sysprof-window.c +++ b/src/sysprof/sysprof-window.c @@ -42,6 +42,8 @@ struct _SysprofWindow SysprofDocument *document; SysprofSession *session; + + GtkToggleButton *show_right_sidebar; }; enum { @@ -138,11 +140,17 @@ main_view_notify_sidebar (SysprofWindow *self, GParamSpec *pspec, AdwOverlaySplitView *main_view) { + GtkWidget *sidebar; + g_assert (SYSPROF_IS_WINDOW (self)); g_assert (ADW_IS_OVERLAY_SPLIT_VIEW (main_view)); - if (adw_overlay_split_view_get_sidebar (main_view) == NULL) + sidebar = adw_overlay_split_view_get_sidebar (main_view); + + if (sidebar == NULL) adw_overlay_split_view_set_show_sidebar (main_view, FALSE); + + gtk_widget_set_sensitive (GTK_WIDGET (self->show_right_sidebar), sidebar != NULL); } static void @@ -224,6 +232,7 @@ sysprof_window_class_init (SysprofWindowClass *klass) gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/sysprof/sysprof-window.ui"); + gtk_widget_class_bind_template_child (widget_class, SysprofWindow, show_right_sidebar); gtk_widget_class_bind_template_callback (widget_class, main_view_notify_sidebar); gtk_widget_class_install_action (widget_class, "win.open-capture", NULL, sysprof_window_open_capture_action); From d649d42aed27c09dac39771a2230d4c541b6ebb6 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 13 Jul 2023 18:02:21 -0700 Subject: [PATCH 0871/1030] libsysprof-analyze: add equality function This is helpful to see if two frames are the same underlying data frame. --- src/libsysprof-analyze/sysprof-document-frame.c | 7 +++++++ src/libsysprof-analyze/sysprof-document-frame.h | 13 ++++++++----- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-document-frame.c b/src/libsysprof-analyze/sysprof-document-frame.c index 00a9b8f1..b47b258e 100644 --- a/src/libsysprof-analyze/sysprof-document-frame.c +++ b/src/libsysprof-analyze/sysprof-document-frame.c @@ -275,3 +275,10 @@ sysprof_document_frame_get_time_offset (SysprofDocumentFrame *self) return self->time_offset; } + +gboolean +sysprof_document_frame_equal (const SysprofDocumentFrame *a, + const SysprofDocumentFrame *b) +{ + return a->frame == b->frame; +} diff --git a/src/libsysprof-analyze/sysprof-document-frame.h b/src/libsysprof-analyze/sysprof-document-frame.h index c8556fa1..279219a1 100644 --- a/src/libsysprof-analyze/sysprof-document-frame.h +++ b/src/libsysprof-analyze/sysprof-document-frame.h @@ -35,15 +35,18 @@ typedef struct _SysprofDocumentFrame SysprofDocumentFrame; typedef struct _SysprofDocumentFrameClass SysprofDocumentFrameClass; SYSPROF_AVAILABLE_IN_ALL -GType sysprof_document_frame_get_type (void) G_GNUC_CONST; +GType sysprof_document_frame_get_type (void) G_GNUC_CONST; SYSPROF_AVAILABLE_IN_ALL -int sysprof_document_frame_get_cpu (SysprofDocumentFrame *self); +int sysprof_document_frame_get_cpu (SysprofDocumentFrame *self); SYSPROF_AVAILABLE_IN_ALL -int sysprof_document_frame_get_pid (SysprofDocumentFrame *self); +int sysprof_document_frame_get_pid (SysprofDocumentFrame *self); SYSPROF_AVAILABLE_IN_ALL -gint64 sysprof_document_frame_get_time (SysprofDocumentFrame *self); +gint64 sysprof_document_frame_get_time (SysprofDocumentFrame *self); SYSPROF_AVAILABLE_IN_ALL -gint64 sysprof_document_frame_get_time_offset (SysprofDocumentFrame *self); +gint64 sysprof_document_frame_get_time_offset (SysprofDocumentFrame *self); +SYSPROF_AVAILABLE_IN_ALL +gboolean sysprof_document_frame_equal (const SysprofDocumentFrame *a, + const SysprofDocumentFrame *b); G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofDocumentFrame, g_object_unref) From 8640a61667dfb399bcc7639ce2963cbe60f5d230 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 13 Jul 2023 18:02:37 -0700 Subject: [PATCH 0872/1030] sysprof: jump to log entry from selection in scrubber --- src/sysprof/sysprof-logs-section.c | 39 +++++++++++++++++++++++++++++ src/sysprof/sysprof-logs-section.ui | 1 + 2 files changed, 40 insertions(+) diff --git a/src/sysprof/sysprof-logs-section.c b/src/sysprof/sysprof-logs-section.c index f5329d0a..484d15f1 100644 --- a/src/sysprof/sysprof-logs-section.c +++ b/src/sysprof/sysprof-logs-section.c @@ -37,6 +37,44 @@ struct _SysprofLogsSection G_DEFINE_FINAL_TYPE (SysprofLogsSection, sysprof_logs_section, SYSPROF_TYPE_SECTION) +static void +sysprof_logs_section_activate_layer_item_cb (SysprofLogsSection *self, + SysprofChartLayer *layer, + SysprofDocumentLog *item, + SysprofChart *chart) +{ + GtkSelectionModel *model; + guint n_items; + + g_assert (SYSPROF_IS_LOGS_SECTION (self)); + g_assert (SYSPROF_IS_CHART_LAYER (layer)); + g_assert (SYSPROF_IS_DOCUMENT_LOG (item)); + g_assert (SYSPROF_IS_CHART (chart)); + + model = gtk_column_view_get_model (self->column_view); + n_items = g_list_model_get_n_items (G_LIST_MODEL (model)); + + for (guint i = 0; i < n_items; i++) + { + g_autoptr(SysprofDocumentLog) log = g_list_model_get_item (G_LIST_MODEL (model), i); + + if (sysprof_document_frame_equal (SYSPROF_DOCUMENT_FRAME (item), SYSPROF_DOCUMENT_FRAME (log))) + { + gtk_single_selection_set_selected (GTK_SINGLE_SELECTION (model), i); + + for (GtkWidget *child = gtk_widget_get_first_child (GTK_WIDGET (self->column_view)); + child != NULL; + child = gtk_widget_get_next_sibling (child)) + { + if (gtk_widget_activate_action (child, "list.scroll-to-item", "u", i)) + break; + } + + break; + } + } +} + static char * format_severity (gpointer unused, GLogLevelFlags severity) @@ -84,6 +122,7 @@ sysprof_logs_section_class_init (SysprofLogsSectionClass *klass) gtk_widget_class_bind_template_child (widget_class, SysprofLogsSection, column_view); gtk_widget_class_bind_template_child (widget_class, SysprofLogsSection, time_column); gtk_widget_class_bind_template_callback (widget_class, format_severity); + gtk_widget_class_bind_template_callback (widget_class, sysprof_logs_section_activate_layer_item_cb); g_type_ensure (SYSPROF_TYPE_DOCUMENT_LOG); g_type_ensure (SYSPROF_TYPE_FRAME_UTILITY); diff --git a/src/sysprof/sysprof-logs-section.ui b/src/sysprof/sysprof-logs-section.ui index b0aa4e4b..07d9651d 100644 --- a/src/sysprof/sysprof-logs-section.ui +++ b/src/sysprof/sysprof-logs-section.ui @@ -13,6 +13,7 @@ 32 + Logs From a8257dbe8161fe9c3c90d3bf3d183381be79664e Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 13 Jul 2023 18:05:18 -0700 Subject: [PATCH 0873/1030] libsysprof-gtk: include CPU in marks table This can be useful when debugging things, so long as the app actually used the active CPU. --- src/libsysprof-gtk/sysprof-mark-table.ui | 35 ++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/libsysprof-gtk/sysprof-mark-table.ui b/src/libsysprof-gtk/sysprof-mark-table.ui index e1b98ae0..98d8f6a9 100644 --- a/src/libsysprof-gtk/sysprof-mark-table.ui +++ b/src/libsysprof-gtk/sysprof-mark-table.ui @@ -77,6 +77,41 @@ +]]> + + + + + + + + CPU + + + + + + + + + + + + + ]]> From f53dda088bd4232cbc4f5133dc0e85390c272578 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 13 Jul 2023 19:52:31 -0700 Subject: [PATCH 0874/1030] libsysprof-gtk: handle eronious ranges better --- src/libsysprof-gtk/sysprof-session.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libsysprof-gtk/sysprof-session.c b/src/libsysprof-gtk/sysprof-session.c index ef5a1b88..4939e758 100644 --- a/src/libsysprof-gtk/sysprof-session.c +++ b/src/libsysprof-gtk/sysprof-session.c @@ -348,7 +348,8 @@ sysprof_session_select_time (SysprofSession *self, if (time_span == NULL) time_span = &document_time_span; - self->selected_time = *time_span; + self->selected_time = sysprof_time_span_order (*time_span); + sysprof_time_span_clamp (&self->selected_time, document_time_span); if (self->visible_time.begin_nsec > time_span->begin_nsec) { From 53e69fceafbed6dc1f982240a4bc8117df414bac Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 13 Jul 2023 19:52:50 -0700 Subject: [PATCH 0875/1030] sysprof: add zoom controls And update them when the visible range changes. --- src/sysprof/sysprof-sidebar.ui | 57 +++++++++++++++--- src/sysprof/sysprof-window.c | 102 +++++++++++++++++++++++++++++++++ 2 files changed, 152 insertions(+), 7 deletions(-) diff --git a/src/sysprof/sysprof-sidebar.ui b/src/sysprof/sysprof-sidebar.ui index d0ac9a68..b0e33009 100644 --- a/src/sysprof/sysprof-sidebar.ui +++ b/src/sysprof/sysprof-sidebar.ui @@ -2,14 +2,57 @@ +]]> + + + + + + + + CPU + + + + + + + + + + + + + +]]> + + + + + + + + PID + + + + + + + + + + + + + ]]> @@ -239,32 +311,5 @@ - - - - - - - - Details - - - - - - - - SysprofLogsSection - - - selection - - - - - - From 6f76432a1730910d6e86842b75c64358735e2c90 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 13 Jul 2023 21:23:21 -0700 Subject: [PATCH 0883/1030] sysprof: add support for bottom up stack traces Keep the "All Processes" and "Process N" nodes, but reverse the stack trace after that point. --- src/libsysprof-analyze/sysprof-callgraph.c | 37 ++++++++++++----- src/libsysprof-analyze/sysprof-callgraph.h | 1 + .../sysprof-callgraph-view-private.h | 1 + src/libsysprof-gtk/sysprof-callgraph-view.c | 41 +++++++++++++++++++ src/libsysprof-gtk/sysprof-callgraph-view.h | 5 +++ src/libsysprof-gtk/sysprof-session.c | 15 +++++++ src/sysprof/sysprof-memory-section.ui | 5 +++ src/sysprof/sysprof-samples-section.ui | 5 +++ src/sysprof/sysprof-window.c | 3 +- src/sysprof/sysprof-window.ui | 4 ++ 10 files changed, 106 insertions(+), 11 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-callgraph.c b/src/libsysprof-analyze/sysprof-callgraph.c index 419f64e9..aa539038 100644 --- a/src/libsysprof-analyze/sysprof-callgraph.c +++ b/src/libsysprof-analyze/sysprof-callgraph.c @@ -226,16 +226,6 @@ sysprof_callgraph_add_trace (SysprofCallgraph *self, parent = &self->root; - /* If the first thing we see is a context switch, then there is - * nothing after it to account for. Just skip the symbol as it - * provides nothing to us in the callgraph. - */ - if (_sysprof_symbol_is_context_switch (symbols[0])) - { - symbols++; - n_symbols--; - } - for (guint i = n_symbols - 1; i > 0; i--) { SysprofSymbol *symbol = symbols[i-1]; @@ -279,6 +269,20 @@ sysprof_callgraph_add_trace (SysprofCallgraph *self, return parent; } +static void +reverse_symbols (SysprofSymbol **symbols, + guint n_symbols) +{ + guint half = n_symbols / 2; + + for (guint i = 0; i < half; i++) + { + SysprofSymbol *tmp = symbols[i]; + symbols[i] = symbols[n_symbols-1-i]; + symbols[n_symbols-1-i] = tmp; + } +} + static void sysprof_callgraph_add_traceable (SysprofCallgraph *self, SysprofDocumentTraceable *traceable, @@ -332,6 +336,19 @@ sysprof_callgraph_add_traceable (SysprofCallgraph *self, if (final_context == SYSPROF_ADDRESS_CONTEXT_KERNEL) symbols[n_symbols++] = _sysprof_document_kernel_symbol (self->document); + /* If the first thing we see is a context switch, then there is + * nothing after it to account for. Just skip the symbol as it + * provides nothing to us in the callgraph. + */ + if (_sysprof_symbol_is_context_switch (symbols[0])) + { + symbols++; + n_symbols--; + } + + if ((self->flags & SYSPROF_CALLGRAPH_FLAGS_BOTTOM_UP) != 0) + reverse_symbols (symbols, n_symbols); + /* If the user requested thread-ids within each process, then * insert a symbol for that before the real stacks. */ diff --git a/src/libsysprof-analyze/sysprof-callgraph.h b/src/libsysprof-analyze/sysprof-callgraph.h index 6c77bd1e..04dc44bf 100644 --- a/src/libsysprof-analyze/sysprof-callgraph.h +++ b/src/libsysprof-analyze/sysprof-callgraph.h @@ -67,6 +67,7 @@ typedef enum _SysprofCallgraphFlags SYSPROF_CALLGRAPH_FLAGS_NONE = 0, SYSPROF_CALLGRAPH_FLAGS_INCLUDE_THREADS = 1 << 1, SYSPROF_CALLGRAPH_FLAGS_HIDE_SYSTEM_LIBRARIES = 1 << 2, + SYSPROF_CALLGRAPH_FLAGS_BOTTOM_UP = 1 << 3, } SysprofCallgraphFlags; SYSPROF_AVAILABLE_IN_ALL diff --git a/src/libsysprof-gtk/sysprof-callgraph-view-private.h b/src/libsysprof-gtk/sysprof-callgraph-view-private.h index 07f84992..5a48d3a7 100644 --- a/src/libsysprof-gtk/sysprof-callgraph-view-private.h +++ b/src/libsysprof-gtk/sysprof-callgraph-view-private.h @@ -50,6 +50,7 @@ struct _SysprofCallgraphView guint reload_source; + guint bottom_up : 1; guint include_threads : 1; guint hide_system_libraries : 1; }; diff --git a/src/libsysprof-gtk/sysprof-callgraph-view.c b/src/libsysprof-gtk/sysprof-callgraph-view.c index 56b437e7..39a0af6c 100644 --- a/src/libsysprof-gtk/sysprof-callgraph-view.c +++ b/src/libsysprof-gtk/sysprof-callgraph-view.c @@ -29,6 +29,7 @@ enum { PROP_0, + PROP_BOTTOM_UP, PROP_CALLGRAPH, PROP_DOCUMENT, PROP_HIDE_SYSTEM_LIBRARIES, @@ -336,6 +337,10 @@ sysprof_callgraph_view_get_property (GObject *object, switch (prop_id) { + case PROP_BOTTOM_UP: + g_value_set_boolean (value, sysprof_callgraph_view_get_bottom_up (self)); + break; + case PROP_CALLGRAPH: g_value_set_object (value, sysprof_callgraph_view_get_callgraph (self)); break; @@ -375,6 +380,10 @@ sysprof_callgraph_view_set_property (GObject *object, switch (prop_id) { + case PROP_BOTTOM_UP: + sysprof_callgraph_view_set_bottom_up (self, g_value_get_boolean (value)); + break; + case PROP_DOCUMENT: sysprof_callgraph_view_set_document (self, g_value_get_object (value)); break; @@ -406,6 +415,11 @@ sysprof_callgraph_view_class_init (SysprofCallgraphViewClass *klass) object_class->get_property = sysprof_callgraph_view_get_property; object_class->set_property = sysprof_callgraph_view_set_property; + properties[PROP_BOTTOM_UP] = + g_param_spec_boolean ("bottom-up", NULL, NULL, + FALSE, + (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + properties[PROP_CALLGRAPH] = g_param_spec_object ("callgraph", NULL, NULL, SYSPROF_TYPE_CALLGRAPH, @@ -579,6 +593,9 @@ sysprof_callgraph_view_reload (SysprofCallgraphView *self) if (self->hide_system_libraries) flags |= SYSPROF_CALLGRAPH_FLAGS_HIDE_SYSTEM_LIBRARIES; + if (self->bottom_up) + flags |= SYSPROF_CALLGRAPH_FLAGS_BOTTOM_UP; + sysprof_document_callgraph_async (self->document, flags, self->traceables, @@ -713,6 +730,30 @@ sysprof_callgraph_view_get_callgraph (SysprofCallgraphView *self) return self->callgraph; } +gboolean +sysprof_callgraph_view_get_bottom_up (SysprofCallgraphView *self) +{ + g_return_val_if_fail (SYSPROF_IS_CALLGRAPH_VIEW (self), FALSE); + + return self->bottom_up; +} + +void +sysprof_callgraph_view_set_bottom_up (SysprofCallgraphView *self, + gboolean bottom_up) +{ + g_return_if_fail (SYSPROF_IS_CALLGRAPH_VIEW (self)); + + bottom_up = !!bottom_up; + + if (self->bottom_up != bottom_up) + { + self->bottom_up = bottom_up; + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_BOTTOM_UP]); + sysprof_callgraph_view_queue_reload (self); + } +} + gboolean sysprof_callgraph_view_get_hide_system_libraries (SysprofCallgraphView *self) { diff --git a/src/libsysprof-gtk/sysprof-callgraph-view.h b/src/libsysprof-gtk/sysprof-callgraph-view.h index 596ae1ab..33177b2b 100644 --- a/src/libsysprof-gtk/sysprof-callgraph-view.h +++ b/src/libsysprof-gtk/sysprof-callgraph-view.h @@ -50,6 +50,11 @@ SYSPROF_AVAILABLE_IN_ALL void sysprof_callgraph_view_set_traceables (SysprofCallgraphView *self, GListModel *model); SYSPROF_AVAILABLE_IN_ALL +gboolean sysprof_callgraph_view_get_bottom_up (SysprofCallgraphView *self); +SYSPROF_AVAILABLE_IN_ALL +void sysprof_callgraph_view_set_bottom_up (SysprofCallgraphView *self, + gboolean bottom_up); +SYSPROF_AVAILABLE_IN_ALL gboolean sysprof_callgraph_view_get_include_threads (SysprofCallgraphView *self); SYSPROF_AVAILABLE_IN_ALL void sysprof_callgraph_view_set_include_threads (SysprofCallgraphView *self, diff --git a/src/libsysprof-gtk/sysprof-session.c b/src/libsysprof-gtk/sysprof-session.c index 4939e758..575d3efc 100644 --- a/src/libsysprof-gtk/sysprof-session.c +++ b/src/libsysprof-gtk/sysprof-session.c @@ -42,12 +42,14 @@ struct _SysprofSession SysprofTimeSpan selected_time; SysprofTimeSpan visible_time; + guint bottom_up : 1; guint include_threads : 1; guint hide_system_libraries : 1; }; enum { PROP_0, + PROP_BOTTOM_UP, PROP_DOCUMENT, PROP_DOCUMENT_TIME, PROP_FILTER, @@ -126,6 +128,10 @@ sysprof_session_get_property (GObject *object, switch (prop_id) { + case PROP_BOTTOM_UP: + g_value_set_boolean (value, self->bottom_up); + break; + case PROP_DOCUMENT: g_value_set_object (value, sysprof_session_get_document (self)); break; @@ -181,6 +187,10 @@ sysprof_session_set_property (GObject *object, switch (prop_id) { + case PROP_BOTTOM_UP: + self->bottom_up = g_value_get_boolean (value); + break; + case PROP_DOCUMENT: sysprof_session_set_document (self, g_value_get_object (value)); break; @@ -207,6 +217,11 @@ sysprof_session_class_init (SysprofSessionClass *klass) object_class->get_property = sysprof_session_get_property; object_class->set_property = sysprof_session_set_property; + properties [PROP_BOTTOM_UP] = + g_param_spec_boolean ("bottom-up", NULL, NULL, + FALSE, + (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + properties [PROP_DOCUMENT] = g_param_spec_object ("document", NULL, NULL, SYSPROF_TYPE_DOCUMENT, diff --git a/src/sysprof/sysprof-memory-section.ui b/src/sysprof/sysprof-memory-section.ui index c3fa7195..eb688dce 100644 --- a/src/sysprof/sysprof-memory-section.ui +++ b/src/sysprof/sysprof-memory-section.ui @@ -76,6 +76,11 @@ SysprofMemorySection + + + SysprofMemorySection + + SysprofMemorySection diff --git a/src/sysprof/sysprof-samples-section.ui b/src/sysprof/sysprof-samples-section.ui index bf031860..f5693b70 100644 --- a/src/sysprof/sysprof-samples-section.ui +++ b/src/sysprof/sysprof-samples-section.ui @@ -76,6 +76,11 @@ SysprofSamplesSection + + + SysprofSamplesSection + + SysprofSamplesSection diff --git a/src/sysprof/sysprof-window.c b/src/sysprof/sysprof-window.c index 41279296..d9b3e70c 100644 --- a/src/sysprof/sysprof-window.c +++ b/src/sysprof/sysprof-window.c @@ -139,8 +139,9 @@ sysprof_window_set_document (SysprofWindow *self, SysprofDocument *document) { static const char *callgraph_actions[] = { - "include-threads", + "bottom-up", "hide-system-libraries", + "include-threads", }; g_assert (SYSPROF_IS_WINDOW (self)); diff --git a/src/sysprof/sysprof-window.ui b/src/sysprof/sysprof-window.ui index 17cb19a0..6abe341a 100644 --- a/src/sysprof/sysprof-window.ui +++ b/src/sysprof/sysprof-window.ui @@ -225,6 +225,10 @@ Include Threads win.callgraph.include-threads + + Bottom Up + win.callgraph.bottom-up + From 791beff1ab8d69033d304d37888f975c0f022b25 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 14 Jul 2023 09:55:07 -0700 Subject: [PATCH 0884/1030] libsysprof-gtk: don't zoom on zero-sized marks --- src/libsysprof-gtk/sysprof-mark-chart-row.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/libsysprof-gtk/sysprof-mark-chart-row.c b/src/libsysprof-gtk/sysprof-mark-chart-row.c index 216f5368..91901f43 100644 --- a/src/libsysprof-gtk/sysprof-mark-chart-row.c +++ b/src/libsysprof-gtk/sysprof-mark-chart-row.c @@ -68,10 +68,15 @@ sysprof_mark_chart_row_activate_layer_item_cb (SysprofMarkChartRow *self, time_span.begin_nsec = sysprof_document_frame_get_time (SYSPROF_DOCUMENT_FRAME (mark)); time_span.end_nsec = sysprof_document_mark_get_end_time (mark); - sysprof_session_select_time (session, &time_span); - sysprof_session_zoom_to_selection (session); + if (sysprof_time_span_duration (time_span) > 0) + { + sysprof_session_select_time (session, &time_span); + sysprof_session_zoom_to_selection (session); - return TRUE; + return TRUE; + } + + return FALSE; } static void From 668249a015737b6799fd984837ba6f591cf34727 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 14 Jul 2023 09:58:29 -0700 Subject: [PATCH 0885/1030] sysprof: binary search for filtered time range in marks scrubber --- src/sysprof/sysprof-marks-section.ui | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/sysprof/sysprof-marks-section.ui b/src/sysprof/sysprof-marks-section.ui index 76f11645..1649db8b 100644 --- a/src/sysprof/sysprof-marks-section.ui +++ b/src/sysprof/sysprof-marks-section.ui @@ -25,13 +25,22 @@ - - - - SysprofMarksSection - - - + + + + + SysprofMarksSection + + + + + + SysprofMarksSection + + + + + From c81e832a5a2363558a22a728dcd349f1c4aafb7b Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 14 Jul 2023 11:29:50 -0700 Subject: [PATCH 0886/1030] sysprof: stub out counters section --- .../scalable/actions/storage-symbolic.svg | 2 + src/sysprof/meson.build | 1 + src/sysprof/sysprof-counters-section.c | 68 ++++++++++++++++++ src/sysprof/sysprof-counters-section.h | 31 +++++++++ src/sysprof/sysprof-counters-section.ui | 69 +++++++++++++++++++ src/sysprof/sysprof-window.c | 2 + src/sysprof/sysprof-window.ui | 30 ++++++++ src/sysprof/sysprof.gresource.xml | 2 + 8 files changed, 205 insertions(+) create mode 100644 src/sysprof/icons/scalable/actions/storage-symbolic.svg create mode 100644 src/sysprof/sysprof-counters-section.c create mode 100644 src/sysprof/sysprof-counters-section.h create mode 100644 src/sysprof/sysprof-counters-section.ui diff --git a/src/sysprof/icons/scalable/actions/storage-symbolic.svg b/src/sysprof/icons/scalable/actions/storage-symbolic.svg new file mode 100644 index 00000000..5417b9a7 --- /dev/null +++ b/src/sysprof/icons/scalable/actions/storage-symbolic.svg @@ -0,0 +1,2 @@ + + diff --git a/src/sysprof/meson.build b/src/sysprof/meson.build index accaab25..7eb71919 100644 --- a/src/sysprof/meson.build +++ b/src/sysprof/meson.build @@ -1,6 +1,7 @@ sysprof_sources = [ 'main.c', 'sysprof-application.c', + 'sysprof-counters-section.c', 'sysprof-cpu-info-dialog.c', 'sysprof-files-section.c', 'sysprof-frame-utility.c', diff --git a/src/sysprof/sysprof-counters-section.c b/src/sysprof/sysprof-counters-section.c new file mode 100644 index 00000000..5d26b104 --- /dev/null +++ b/src/sysprof/sysprof-counters-section.c @@ -0,0 +1,68 @@ +/* sysprof-counters-section.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include + +#include "sysprof-counters-section.h" + +struct _SysprofCountersSection +{ + SysprofSection parent_instance; +}; + +G_DEFINE_FINAL_TYPE (SysprofCountersSection, sysprof_counters_section, SYSPROF_TYPE_SECTION) + +static void +sysprof_counters_section_dispose (GObject *object) +{ + SysprofCountersSection *self = (SysprofCountersSection *)object; + + gtk_widget_dispose_template (GTK_WIDGET (self), SYSPROF_TYPE_COUNTERS_SECTION); + + G_OBJECT_CLASS (sysprof_counters_section_parent_class)->dispose (object); +} + +static void +sysprof_counters_section_class_init (SysprofCountersSectionClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->dispose = sysprof_counters_section_dispose; + + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/sysprof/sysprof-counters-section.ui"); + + g_type_ensure (SYSPROF_TYPE_CHART); + g_type_ensure (SYSPROF_TYPE_DOCUMENT_MARK); + g_type_ensure (SYSPROF_TYPE_DOCUMENT_COUNTER); + g_type_ensure (SYSPROF_TYPE_DOCUMENT_COUNTER_VALUE); + g_type_ensure (SYSPROF_TYPE_LINE_LAYER); + g_type_ensure (SYSPROF_TYPE_TIME_SERIES); + g_type_ensure (SYSPROF_TYPE_TIME_SPAN_LAYER); + g_type_ensure (SYSPROF_TYPE_XY_SERIES); +} + +static void +sysprof_counters_section_init (SysprofCountersSection *self) +{ + gtk_widget_init_template (GTK_WIDGET (self)); +} diff --git a/src/sysprof/sysprof-counters-section.h b/src/sysprof/sysprof-counters-section.h new file mode 100644 index 00000000..544fa8bc --- /dev/null +++ b/src/sysprof/sysprof-counters-section.h @@ -0,0 +1,31 @@ +/* sysprof-counters-section.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-section.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_COUNTERS_SECTION (sysprof_counters_section_get_type()) + +G_DECLARE_FINAL_TYPE (SysprofCountersSection, sysprof_counters_section, SYSPROF, COUNTERS_SECTION, SysprofSection) + +G_END_DECLS diff --git a/src/sysprof/sysprof-counters-section.ui b/src/sysprof/sysprof-counters-section.ui new file mode 100644 index 00000000..2f0cabbc --- /dev/null +++ b/src/sysprof/sysprof-counters-section.ui @@ -0,0 +1,69 @@ + + + + + + diff --git a/src/sysprof/sysprof-window.c b/src/sysprof/sysprof-window.c index d9b3e70c..4c27792d 100644 --- a/src/sysprof/sysprof-window.c +++ b/src/sysprof/sysprof-window.c @@ -24,6 +24,7 @@ #include +#include "sysprof-counters-section.h" #include "sysprof-cpu-info-dialog.h" #include "sysprof-files-section.h" #include "sysprof-greeter.h" @@ -412,6 +413,7 @@ sysprof_window_class_init (SysprofWindowClass *klass) gtk_widget_class_add_binding_action (widget_class, GDK_KEY_bracketleft, GDK_CONTROL_MASK, "session.seek-backward", NULL); gtk_widget_class_add_binding_action (widget_class, GDK_KEY_bracketright, GDK_CONTROL_MASK, "session.seek-forward", NULL); + g_type_ensure (SYSPROF_TYPE_COUNTERS_SECTION); g_type_ensure (SYSPROF_TYPE_DOCUMENT); g_type_ensure (SYSPROF_TYPE_FILES_SECTION); g_type_ensure (SYSPROF_TYPE_LOGS_SECTION); diff --git a/src/sysprof/sysprof-window.ui b/src/sysprof/sysprof-window.ui index 6abe341a..79af664a 100644 --- a/src/sysprof/sysprof-window.ui +++ b/src/sysprof/sysprof-window.ui @@ -143,6 +143,36 @@ + + + CPU Usage + counters + system-run-symbolic + + SysprofWindow + + + + + + Network + counters + network-transmit-receive-symbolic + + SysprofWindow + + + + + + Storage + counters + storage-symbolic + + SysprofWindow + + + auxiliary diff --git a/src/sysprof/sysprof.gresource.xml b/src/sysprof/sysprof.gresource.xml index 6ecbdc6b..37a1d126 100644 --- a/src/sysprof/sysprof.gresource.xml +++ b/src/sysprof/sysprof.gresource.xml @@ -3,6 +3,7 @@ gtk/help-overlay.ui gtk/menus.ui + sysprof-counters-section.ui sysprof-cpu-info-dialog.ui sysprof-files-section.ui sysprof-frame-utility.ui @@ -25,6 +26,7 @@ icons/scalable/actions/memory-allocations-symbolic.svg icons/scalable/actions/metadata-symbolic.svg icons/scalable/actions/process-mounts-symbolic.svg + icons/scalable/actions/storage-symbolic.svg icons/scalable/actions/system-log-symbolic.svg From 6afce43cc5120990da2375d45f61875201b4e337 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 14 Jul 2023 11:34:29 -0700 Subject: [PATCH 0887/1030] sysprof: remove separator --- src/sysprof/sysprof-counters-section.ui | 4 ---- src/sysprof/sysprof-marks-section.ui | 4 ---- 2 files changed, 8 deletions(-) diff --git a/src/sysprof/sysprof-counters-section.ui b/src/sysprof/sysprof-counters-section.ui index 2f0cabbc..63df9816 100644 --- a/src/sysprof/sysprof-counters-section.ui +++ b/src/sysprof/sysprof-counters-section.ui @@ -18,10 +18,6 @@ - - - - true diff --git a/src/sysprof/sysprof-marks-section.ui b/src/sysprof/sysprof-marks-section.ui index 1649db8b..6fb34294 100644 --- a/src/sysprof/sysprof-marks-section.ui +++ b/src/sysprof/sysprof-marks-section.ui @@ -55,10 +55,6 @@ - - - - true From 15df36f6c2ac23ce30f6333d240256a57a7ec508 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 14 Jul 2023 13:11:37 -0700 Subject: [PATCH 0888/1030] libsysprof-analyze: add tooltip helpers to data models --- .../sysprof-document-frame-private.h | 1 + .../sysprof-document-frame.c | 79 +++++++++++++++++++ .../sysprof-document-frame.h | 18 +++-- .../sysprof-document-mark.c | 18 +++++ 4 files changed, 109 insertions(+), 7 deletions(-) diff --git a/src/libsysprof-analyze/sysprof-document-frame-private.h b/src/libsysprof-analyze/sysprof-document-frame-private.h index a7e0e5b2..b743b549 100644 --- a/src/libsysprof-analyze/sysprof-document-frame-private.h +++ b/src/libsysprof-analyze/sysprof-document-frame-private.h @@ -41,6 +41,7 @@ struct _SysprofDocumentFrameClass { GObjectClass parent_class; const char *type_name; + char *(*dup_tooltip) (SysprofDocumentFrame *self); }; SysprofDocumentFrame *_sysprof_document_frame_new (GMappedFile *mapped, diff --git a/src/libsysprof-analyze/sysprof-document-frame.c b/src/libsysprof-analyze/sysprof-document-frame.c index b47b258e..c191a35c 100644 --- a/src/libsysprof-analyze/sysprof-document-frame.c +++ b/src/libsysprof-analyze/sysprof-document-frame.c @@ -47,12 +47,24 @@ enum { PROP_PID, PROP_TIME, PROP_TIME_OFFSET, + PROP_TIME_STRING, + PROP_TOOLTIP, PROP_TYPE_NAME, N_PROPS }; static GParamSpec *properties[N_PROPS]; +static char * +sysprof_document_frame_real_dup_tooltip (SysprofDocumentFrame *self) +{ + g_autofree char *time_string = sysprof_document_frame_dup_time_string (self); + + return g_strdup_printf ("%s: %s", + time_string, + SYSPROF_DOCUMENT_FRAME_GET_CLASS (self)->type_name); +} + static const char * sysprof_document_frame_get_type_name (SysprofDocumentFrame *self) { @@ -98,6 +110,14 @@ sysprof_document_frame_get_property (GObject *object, g_value_set_int64 (value, sysprof_document_frame_get_time_offset (self)); break; + case PROP_TIME_STRING: + g_value_take_string (value, sysprof_document_frame_dup_time_string (self)); + break; + + case PROP_TOOLTIP: + g_value_take_string (value, sysprof_document_frame_dup_tooltip (self)); + break; + case PROP_TYPE_NAME: g_value_set_static_string (value, sysprof_document_frame_get_type_name (self)); break; @@ -116,6 +136,7 @@ sysprof_document_frame_class_init (SysprofDocumentFrameClass *klass) object_class->get_property = sysprof_document_frame_get_property; klass->type_name = N_("Frame"); + klass->dup_tooltip = sysprof_document_frame_real_dup_tooltip; properties[PROP_CPU] = g_param_spec_int ("cpu", NULL, NULL, @@ -145,11 +166,21 @@ sysprof_document_frame_class_init (SysprofDocumentFrameClass *klass) 0, (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + properties[PROP_TIME_STRING] = + g_param_spec_string ("time-string", NULL, NULL, + NULL, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + properties[PROP_TYPE_NAME] = g_param_spec_string ("type-name", NULL, NULL, NULL, (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + properties[PROP_TOOLTIP] = + g_param_spec_string ("tooltip", NULL, NULL, + NULL, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_properties (object_class, N_PROPS, properties); } @@ -282,3 +313,51 @@ sysprof_document_frame_equal (const SysprofDocumentFrame *a, { return a->frame == b->frame; } + +/** + * sysprof_document_frame_dup_time_string: + * @self: a #SysprofDocumentFrame + * + * Gets the time formatted as a string such as `00:00:00.1234`. + * + * Returns: (transfer full): a new string + */ +char * +sysprof_document_frame_dup_time_string (SysprofDocumentFrame *self) +{ + int hours; + int minutes; + int seconds; + double time; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_FRAME (self), NULL); + + time = self->time_offset / (double)SYSPROF_NSEC_PER_SEC; + + hours = time / (60 * 60); + time -= hours * (60 * 60); + + minutes = time / 60; + time -= minutes * 60; + + seconds = time / SYSPROF_NSEC_PER_SEC; + time -= seconds * SYSPROF_NSEC_PER_SEC; + + return g_strdup_printf ("%02d:%02d:%02d.%04d", hours, minutes, seconds, (int)(time * 10000)); +} + +/** + * sysprof_document_frame_dup_tooltip: + * @self: a #SysprofDocumentFrame + * + * Returns a new string containing suggested tooltip text. + * + * Returns: (transfer full): a string + */ +char * +sysprof_document_frame_dup_tooltip (SysprofDocumentFrame *self) +{ + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_FRAME (self), NULL); + + return SYSPROF_DOCUMENT_FRAME_GET_CLASS (self)->dup_tooltip (self); +} diff --git a/src/libsysprof-analyze/sysprof-document-frame.h b/src/libsysprof-analyze/sysprof-document-frame.h index 279219a1..57662960 100644 --- a/src/libsysprof-analyze/sysprof-document-frame.h +++ b/src/libsysprof-analyze/sysprof-document-frame.h @@ -35,18 +35,22 @@ typedef struct _SysprofDocumentFrame SysprofDocumentFrame; typedef struct _SysprofDocumentFrameClass SysprofDocumentFrameClass; SYSPROF_AVAILABLE_IN_ALL -GType sysprof_document_frame_get_type (void) G_GNUC_CONST; +GType sysprof_document_frame_get_type (void) G_GNUC_CONST; SYSPROF_AVAILABLE_IN_ALL -int sysprof_document_frame_get_cpu (SysprofDocumentFrame *self); +int sysprof_document_frame_get_cpu (SysprofDocumentFrame *self); SYSPROF_AVAILABLE_IN_ALL -int sysprof_document_frame_get_pid (SysprofDocumentFrame *self); +int sysprof_document_frame_get_pid (SysprofDocumentFrame *self); SYSPROF_AVAILABLE_IN_ALL -gint64 sysprof_document_frame_get_time (SysprofDocumentFrame *self); +gint64 sysprof_document_frame_get_time (SysprofDocumentFrame *self); SYSPROF_AVAILABLE_IN_ALL -gint64 sysprof_document_frame_get_time_offset (SysprofDocumentFrame *self); +gint64 sysprof_document_frame_get_time_offset (SysprofDocumentFrame *self); SYSPROF_AVAILABLE_IN_ALL -gboolean sysprof_document_frame_equal (const SysprofDocumentFrame *a, - const SysprofDocumentFrame *b); +gboolean sysprof_document_frame_equal (const SysprofDocumentFrame *a, + const SysprofDocumentFrame *b); +SYSPROF_AVAILABLE_IN_ALL +char *sysprof_document_frame_dup_tooltip (SysprofDocumentFrame *self); +SYSPROF_AVAILABLE_IN_ALL +char *sysprof_document_frame_dup_time_string (SysprofDocumentFrame *self); G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofDocumentFrame, g_object_unref) diff --git a/src/libsysprof-analyze/sysprof-document-mark.c b/src/libsysprof-analyze/sysprof-document-mark.c index 712fd14d..66698fe6 100644 --- a/src/libsysprof-analyze/sysprof-document-mark.c +++ b/src/libsysprof-analyze/sysprof-document-mark.c @@ -50,6 +50,23 @@ G_DEFINE_FINAL_TYPE (SysprofDocumentMark, sysprof_document_mark, SYSPROF_TYPE_DO static GParamSpec *properties [N_PROPS]; +static char * +sysprof_document_mark_dup_tooltip (SysprofDocumentFrame *frame) +{ + SysprofDocumentMark *self = (SysprofDocumentMark *)frame; + g_autofree char *time_string = NULL; + + g_assert (SYSPROF_IS_DOCUMENT_MARK (self)); + + time_string = sysprof_document_frame_dup_time_string (SYSPROF_DOCUMENT_FRAME (self)); + + return g_strdup_printf ("%s: %s: %s: %s", + time_string, + sysprof_document_mark_get_group (self), + sysprof_document_mark_get_name (self), + sysprof_document_mark_get_message (self)); +} + static void sysprof_document_mark_get_property (GObject *object, guint prop_id, @@ -94,6 +111,7 @@ sysprof_document_mark_class_init (SysprofDocumentMarkClass *klass) object_class->get_property = sysprof_document_mark_get_property; document_frame_class->type_name = N_("Mark"); + document_frame_class->dup_tooltip = sysprof_document_mark_dup_tooltip; properties [PROP_DURATION] = g_param_spec_int64 ("duration", NULL, NULL, From 1ad6f66b9e5d997d6070164c28deca3243a5ee5d Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 14 Jul 2023 12:20:13 -0700 Subject: [PATCH 0889/1030] libsysprof-gtk: improve session model robustness Handle items-changed, ensure we calculate items-changed at setup time correctly when late binding is in use. --- src/libsysprof-gtk/sysprof-session-model.c | 70 +++++++++++++++------- 1 file changed, 47 insertions(+), 23 deletions(-) diff --git a/src/libsysprof-gtk/sysprof-session-model.c b/src/libsysprof-gtk/sysprof-session-model.c index e59bb40e..be47559b 100644 --- a/src/libsysprof-gtk/sysprof-session-model.c +++ b/src/libsysprof-gtk/sysprof-session-model.c @@ -27,6 +27,7 @@ struct _SysprofSessionModel { GObject parent_instance; GListModel *model; + GSignalGroup *model_signals; SysprofSession *session; }; @@ -67,7 +68,7 @@ sysprof_session_model_get_n_items (GListModel *model) { SysprofSessionModel *self = SYSPROF_SESSION_MODEL (model); - if (self->model == NULL) + if (self->model == NULL || self->session == 0) return 0; return g_list_model_get_n_items (self->model); @@ -86,11 +87,27 @@ G_DEFINE_FINAL_TYPE_WITH_CODE (SysprofSessionModel, sysprof_session_model, G_TYP static GParamSpec *properties [N_PROPS]; +static void +sysprof_session_model_items_changed_cb (SysprofSessionModel *self, + guint position, + guint removed, + guint added, + GListModel *model) +{ + g_assert (SYSPROF_IS_SESSION_MODEL (self)); + + if (self->session == NULL || self->model != model) + return; + + g_list_model_items_changed (G_LIST_MODEL (self), position, removed, added); +} + static void sysprof_session_model_dispose (GObject *object) { SysprofSessionModel *self = (SysprofSessionModel *)object; + g_clear_object (&self->model_signals); g_clear_object (&self->session); g_clear_object (&self->model); @@ -168,6 +185,12 @@ sysprof_session_model_class_init (SysprofSessionModelClass *klass) static void sysprof_session_model_init (SysprofSessionModel *self) { + self->model_signals = g_signal_group_new (G_TYPE_LIST_MODEL); + g_signal_group_connect_object (self->model_signals, + "items-changed", + G_CALLBACK (sysprof_session_model_items_changed_cb), + self, + G_CONNECT_SWAPPED); } SysprofSessionModel * @@ -201,8 +224,8 @@ void sysprof_session_model_set_model (SysprofSessionModel *self, GListModel *model) { - guint old_len = 0; - guint new_len = 0; + guint old_n_items = 0; + guint new_n_items = 0; g_return_if_fail (SYSPROF_IS_SESSION_MODEL (self)); g_return_if_fail (!model || G_IS_LIST_MODEL (model)); @@ -210,22 +233,18 @@ sysprof_session_model_set_model (SysprofSessionModel *self, if (self->model == model) return; - if (self->model) - { - old_len = g_list_model_get_n_items (G_LIST_MODEL (self->model)); - g_clear_object (&self->model); - } - if (model) - { - self->model = g_object_ref (model); - new_len = g_list_model_get_n_items (model); - } + g_object_ref (model); + + old_n_items = g_list_model_get_n_items (G_LIST_MODEL (self)); + g_set_object (&self->model, model); + g_signal_group_set_target (self->model_signals, model); + new_n_items = g_list_model_get_n_items (G_LIST_MODEL (self)); + + if (old_n_items || new_n_items) + g_list_model_items_changed (G_LIST_MODEL (self), 0, old_n_items, new_n_items); g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODEL]); - - if (old_len || new_len) - g_list_model_items_changed (G_LIST_MODEL (self), 0, old_len, new_len); } /** @@ -246,16 +265,21 @@ void sysprof_session_model_set_session (SysprofSessionModel *self, SysprofSession *session) { + guint old_n_items = 0; + guint new_n_items = 0; + g_return_if_fail (SYSPROF_IS_SESSION_MODEL (self)); g_return_if_fail (!session || SYSPROF_IS_SESSION (session)); - if (g_set_object (&self->session, session)) - { - guint n_items = g_list_model_get_n_items (G_LIST_MODEL (self)); + if (self->session == session) + return; - if (n_items > 0) - g_list_model_items_changed (G_LIST_MODEL (self), 0, n_items, n_items); + old_n_items = g_list_model_get_n_items (G_LIST_MODEL (self)); + g_set_object (&self->session, session); + new_n_items = g_list_model_get_n_items (G_LIST_MODEL (self)); - g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SESSION]); - } + if (old_n_items || new_n_items) + g_list_model_items_changed (G_LIST_MODEL (self), 0, old_n_items, new_n_items); + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SESSION]); } From d5320065c6b81fb9a78f3c0eadcb22374944b9ff Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 14 Jul 2023 12:01:16 -0700 Subject: [PATCH 0890/1030] sysprof: add a waterfall to the marks section --- .../actions/mark-waterfall-symbolic.svg | 51 +++++++++ src/sysprof/sysprof-marks-section.c | 2 + src/sysprof/sysprof-marks-section.ui | 102 ++++++++++++++++++ src/sysprof/sysprof.gresource.xml | 1 + 4 files changed, 156 insertions(+) create mode 100644 src/sysprof/icons/scalable/actions/mark-waterfall-symbolic.svg diff --git a/src/sysprof/icons/scalable/actions/mark-waterfall-symbolic.svg b/src/sysprof/icons/scalable/actions/mark-waterfall-symbolic.svg new file mode 100644 index 00000000..cf68d97b --- /dev/null +++ b/src/sysprof/icons/scalable/actions/mark-waterfall-symbolic.svg @@ -0,0 +1,51 @@ + + + + + + + + + diff --git a/src/sysprof/sysprof-marks-section.c b/src/sysprof/sysprof-marks-section.c index f3200090..e657b0a3 100644 --- a/src/sysprof/sysprof-marks-section.c +++ b/src/sysprof/sysprof-marks-section.c @@ -60,6 +60,8 @@ sysprof_marks_section_class_init (SysprofMarksSectionClass *klass) g_type_ensure (SYSPROF_TYPE_DOCUMENT_MARK); g_type_ensure (SYSPROF_TYPE_MARK_CHART); g_type_ensure (SYSPROF_TYPE_MARK_TABLE); + g_type_ensure (SYSPROF_TYPE_SESSION_MODEL); + g_type_ensure (SYSPROF_TYPE_SESSION_MODEL_ITEM); g_type_ensure (SYSPROF_TYPE_TIME_SERIES); g_type_ensure (SYSPROF_TYPE_TIME_SPAN_LAYER); } diff --git a/src/sysprof/sysprof-marks-section.ui b/src/sysprof/sysprof-marks-section.ui index 6fb34294..e870b548 100644 --- a/src/sysprof/sysprof-marks-section.ui +++ b/src/sysprof/sysprof-marks-section.ui @@ -84,6 +84,108 @@ + + + Mark Waterfall + mark-waterfall-symbolic + + + + + + + + + + SysprofMarksSection + + + + + + SysprofMarksSection + + + + + + SysprofMarksSection + + + + + + + + + + + + + + + +]]> + + + + + + + + + diff --git a/src/sysprof/sysprof.gresource.xml b/src/sysprof/sysprof.gresource.xml index 37a1d126..032728d2 100644 --- a/src/sysprof/sysprof.gresource.xml +++ b/src/sysprof/sysprof.gresource.xml @@ -28,5 +28,6 @@ icons/scalable/actions/process-mounts-symbolic.svg icons/scalable/actions/storage-symbolic.svg icons/scalable/actions/system-log-symbolic.svg + icons/scalable/actions/mark-waterfall-symbolic.svg From 9e40096675f39ffb42dcc42ca0ef8adafea8b01e Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 14 Jul 2023 14:12:16 -0700 Subject: [PATCH 0891/1030] sysprof: tweak waterfall icon --- .../actions/mark-waterfall-symbolic.svg | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/sysprof/icons/scalable/actions/mark-waterfall-symbolic.svg b/src/sysprof/icons/scalable/actions/mark-waterfall-symbolic.svg index cf68d97b..0a234526 100644 --- a/src/sysprof/icons/scalable/actions/mark-waterfall-symbolic.svg +++ b/src/sysprof/icons/scalable/actions/mark-waterfall-symbolic.svg @@ -5,7 +5,7 @@ width="16px" version="1.1" id="svg5033" - sodipodi:docname="mark-table-symbolic.svg" + sodipodi:docname="mark-waterfall-symbolic.svg" inkscape:version="1.2.2 (b0a8486541, 2022-12-01)" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" @@ -23,11 +23,11 @@ inkscape:pagecheckerboard="0" inkscape:deskcolor="#d1d1d1" showgrid="false" - inkscape:zoom="5.6734271" - inkscape:cx="-88.218284" - inkscape:cy="69.446561" - inkscape:window-width="2560" - inkscape:window-height="1371" + inkscape:zoom="2.2542364" + inkscape:cx="39.924828" + inkscape:cy="6.2105288" + inkscape:window-width="1920" + inkscape:window-height="1011" inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="1" @@ -37,13 +37,13 @@ id="path5029" style="stroke-width:1.4676" /> + style="stroke-width:0.877068" /> + style="stroke-width:1.1094" /> Date: Fri, 14 Jul 2023 14:41:00 -0700 Subject: [PATCH 0892/1030] libsysprof-gtk: fix styling of progress cell with backdrop --- src/libsysprof-gtk/style.css | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libsysprof-gtk/style.css b/src/libsysprof-gtk/style.css index d02d805d..66d42ea6 100644 --- a/src/libsysprof-gtk/style.css +++ b/src/libsysprof-gtk/style.css @@ -13,6 +13,9 @@ progresscell progress:backdrop { background: @borders; border-color: shade(@borders, .9); } +progresscell label:backdrop.in-progress { + color: inherit; +} progresscell trough { background: alpha(@borders, .5); From 7ff844d4ec4fbb04238aabb76eb6068221806214 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 14 Jul 2023 15:07:17 -0700 Subject: [PATCH 0893/1030] libsysprof-analyze: add bitset index n-items property --- .../sysprof-document-bitset-index.c | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/libsysprof-analyze/sysprof-document-bitset-index.c b/src/libsysprof-analyze/sysprof-document-bitset-index.c index a2386853..112cb6e6 100644 --- a/src/libsysprof-analyze/sysprof-document-bitset-index.c +++ b/src/libsysprof-analyze/sysprof-document-bitset-index.c @@ -78,6 +78,33 @@ list_model_iface_init (GListModelInterface *iface) G_DEFINE_FINAL_TYPE_WITH_CODE (SysprofDocumentBitsetIndex, sysprof_document_bitset_index, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init)) +enum { + PROP_0, + PROP_N_ITEMS, + N_PROPS +}; + +static GParamSpec *properties[N_PROPS]; + +static void +sysprof_document_bitset_index_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofDocumentBitsetIndex *self = SYSPROF_DOCUMENT_BITSET_INDEX (object); + + switch (prop_id) + { + case PROP_N_ITEMS: + g_value_set_uint (value, g_list_model_get_n_items (G_LIST_MODEL (self))); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + static void sysprof_document_bitset_index_dispose (GObject *object) { @@ -95,6 +122,14 @@ sysprof_document_bitset_index_class_init (SysprofDocumentBitsetIndexClass *klass GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->dispose = sysprof_document_bitset_index_dispose; + object_class->get_property = sysprof_document_bitset_index_get_property; + + properties[PROP_N_ITEMS] = + g_param_spec_uint ("n-items", NULL, NULL, + 0, G_MAXUINT, 0, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); } static void From 2146e16931fb768997bf5eac8b5a31302763f4c1 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 14 Jul 2023 15:08:15 -0700 Subject: [PATCH 0894/1030] sysprof: add indicators to various sections This helps seeing at a glance if a recording when a bit sideways. --- src/libsysprof-gtk/style.css | 10 +++++++ src/sysprof/sysprof-logs-section.c | 10 +++++++ src/sysprof/sysprof-logs-section.ui | 11 +++++++ src/sysprof/sysprof-marks-section.c | 8 +++++ src/sysprof/sysprof-marks-section.ui | 11 +++++++ src/sysprof/sysprof-processes-section.c | 8 +++++ src/sysprof/sysprof-processes-section.ui | 11 +++++++ src/sysprof/sysprof-section.c | 38 ++++++++++++++++++++++++ src/sysprof/sysprof-section.h | 3 ++ src/sysprof/sysprof-sidebar.c | 25 ++++++++++++++++ 10 files changed, 135 insertions(+) diff --git a/src/libsysprof-gtk/style.css b/src/libsysprof-gtk/style.css index 66d42ea6..576f12cf 100644 --- a/src/libsysprof-gtk/style.css +++ b/src/libsysprof-gtk/style.css @@ -108,3 +108,13 @@ tracks row track info { .utility .view { background: transparent; } + +.navigation-sidebar label.indicator { + background: alpha(@accent_bg_color, .5); + color: @accent_fg_color; + border-radius: 50px; + padding: 1px 5px; + font-feature-settings: 'tnum'; + font-size: .8em; + border: 1px solid alpha(@accent_bg_color,.6); +} diff --git a/src/sysprof/sysprof-logs-section.c b/src/sysprof/sysprof-logs-section.c index 484d15f1..3428c6f3 100644 --- a/src/sysprof/sysprof-logs-section.c +++ b/src/sysprof/sysprof-logs-section.c @@ -24,6 +24,7 @@ #include +#include "sysprof-document-bitset-index-private.h" #include "sysprof-frame-utility.h" #include "sysprof-logs-section.h" @@ -75,6 +76,13 @@ sysprof_logs_section_activate_layer_item_cb (SysprofLogsSection *self, } } +static char * +format_number (gpointer unused, + guint number) +{ + return g_strdup_printf ("%'u", number); +} + static char * format_severity (gpointer unused, GLogLevelFlags severity) @@ -121,9 +129,11 @@ sysprof_logs_section_class_init (SysprofLogsSectionClass *klass) gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/sysprof/sysprof-logs-section.ui"); gtk_widget_class_bind_template_child (widget_class, SysprofLogsSection, column_view); gtk_widget_class_bind_template_child (widget_class, SysprofLogsSection, time_column); + gtk_widget_class_bind_template_callback (widget_class, format_number); gtk_widget_class_bind_template_callback (widget_class, format_severity); gtk_widget_class_bind_template_callback (widget_class, sysprof_logs_section_activate_layer_item_cb); + g_type_ensure (SYSPROF_TYPE_DOCUMENT_BITSET_INDEX); g_type_ensure (SYSPROF_TYPE_DOCUMENT_LOG); g_type_ensure (SYSPROF_TYPE_FRAME_UTILITY); g_type_ensure (SYSPROF_TYPE_TIME_LABEL); diff --git a/src/sysprof/sysprof-logs-section.ui b/src/sysprof/sysprof-logs-section.ui index 3329b3c1..aa44b627 100644 --- a/src/sysprof/sysprof-logs-section.ui +++ b/src/sysprof/sysprof-logs-section.ui @@ -2,6 +2,17 @@