From 131d9fba298e1bc7e3cbdab6097e68b87a0771ee Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 15 May 2023 13:30:41 -0700 Subject: [PATCH] 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); }