From b3a4c295c346074af181610028e8bd92bb0b5b84 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 10 May 2023 15:14:09 -0700 Subject: [PATCH] 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; }