From 874fb01c252a736337776ad7b02f5006c983714e Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 27 May 2019 18:05:15 -0700 Subject: [PATCH] libsysprof: use embedded kallsyms when possible This uses the kallysms that has been embedded in the capture file when that is possible (such as when proc-source appends it). --- src/libsysprof/meson.build | 3 +- src/libsysprof/sysprof-kallsyms.h | 6 +- .../sysprof-kernel-symbol-resolver.c | 84 ++++++++++- src/libsysprof/sysprof-kernel-symbol.c | 131 ++++++++++-------- src/libsysprof/sysprof-kernel-symbol.h | 5 +- src/libsysprof/sysprof-private.h | 39 ++++++ src/libsysprof/sysprof.h | 1 - src/tests/meson.build | 6 - src/tests/test-kallsyms.c | 2 + 9 files changed, 195 insertions(+), 82 deletions(-) create mode 100644 src/libsysprof/sysprof-private.h diff --git a/src/libsysprof/meson.build b/src/libsysprof/meson.build index 727721a2..d225d0cc 100644 --- a/src/libsysprof/meson.build +++ b/src/libsysprof/meson.build @@ -6,7 +6,6 @@ libsysprof_public_sources = [ 'sysprof-elf-symbol-resolver.c', 'sysprof-hostinfo-source.c', 'sysprof-jitmap-symbol-resolver.c', - 'sysprof-kallsyms.c', 'sysprof-kernel-symbol.c', 'sysprof-kernel-symbol-resolver.c', 'sysprof-local-profiler.c', @@ -29,7 +28,6 @@ libsysprof_public_headers = [ 'sysprof-elf-symbol-resolver.h', 'sysprof-hostinfo-source.h', 'sysprof-jitmap-symbol-resolver.h', - 'sysprof-kallsyms.h', 'sysprof-kernel-symbol.h', 'sysprof-kernel-symbol-resolver.h', 'sysprof-local-profiler.h', @@ -52,6 +50,7 @@ libsysprof_private_sources = [ 'demangle.cpp', 'elfparser.c', 'sysprof-helpers.c', + 'sysprof-kallsyms.c', 'sysprof-line-reader.c', ipc_service_src, stackstash_sources, diff --git a/src/libsysprof/sysprof-kallsyms.h b/src/libsysprof/sysprof-kallsyms.h index 1778f5b2..c50303ea 100644 --- a/src/libsysprof/sysprof-kallsyms.h +++ b/src/libsysprof/sysprof-kallsyms.h @@ -20,22 +20,18 @@ #pragma once -#include "sysprof-version-macros.h" +#include G_BEGIN_DECLS typedef struct _SysprofKallsyms SysprofKallsyms; -SYSPROF_AVAILABLE_IN_ALL SysprofKallsyms *sysprof_kallsyms_new (const gchar *path); -SYSPROF_AVAILABLE_IN_ALL SysprofKallsyms *sysprof_kallsyms_new_take (gchar *data); -SYSPROF_AVAILABLE_IN_ALL gboolean sysprof_kallsyms_next (SysprofKallsyms *self, const gchar **name, guint64 *address, guint8 *type); -SYSPROF_AVAILABLE_IN_ALL void sysprof_kallsyms_free (SysprofKallsyms *self); G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofKallsyms, sysprof_kallsyms_free) diff --git a/src/libsysprof/sysprof-kernel-symbol-resolver.c b/src/libsysprof/sysprof-kernel-symbol-resolver.c index 4e0f8e24..ee6ec82e 100644 --- a/src/libsysprof/sysprof-kernel-symbol-resolver.c +++ b/src/libsysprof/sysprof-kernel-symbol-resolver.c @@ -18,14 +18,23 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ +#define G_LOG_DOMAIN "sysprof-kernel-symbol-resolver" + #include "config.h" +#include + +#include "sysprof-kallsyms.h" #include "sysprof-kernel-symbol.h" #include "sysprof-kernel-symbol-resolver.h" +#include "sysprof-private.h" + +#include "sysprof-platform.h" struct _SysprofKernelSymbolResolver { - GObject parent_instance; + GObject parent_instance; + SysprofKernelSymbols *symbols; }; static GQuark linux_quark; @@ -38,16 +47,19 @@ sysprof_kernel_symbol_resolver_resolve_with_context (SysprofSymbolResolver *reso SysprofCaptureAddress address, GQuark *tag) { + SysprofKernelSymbolResolver *self = (SysprofKernelSymbolResolver *)resolver; const SysprofKernelSymbol *sym; - g_assert (SYSPROF_IS_SYMBOL_RESOLVER (resolver)); + g_assert (SYSPROF_IS_SYMBOL_RESOLVER (self)); + g_assert (tag != NULL); if (context != SYSPROF_ADDRESS_CONTEXT_KERNEL) return NULL; - sym = sysprof_kernel_symbol_from_address (address); + if (self->symbols == NULL) + return NULL; - if (sym != NULL) + if ((sym = _sysprof_kernel_symbols_lookup (self->symbols, address))) { *tag = linux_quark; return g_strdup (sym->name); @@ -56,9 +68,59 @@ sysprof_kernel_symbol_resolver_resolve_with_context (SysprofSymbolResolver *reso return NULL; } +static void +sysprof_kernel_symbol_resolver_load (SysprofSymbolResolver *resolver, + SysprofCaptureReader *reader) +{ + static const guint8 zero[] = {0}; + SysprofKernelSymbolResolver *self = (SysprofKernelSymbolResolver *)resolver; + g_autoptr(GByteArray) bytes = NULL; + g_autoptr(SysprofKallsyms) kallsyms = NULL; + guint8 buf[4096]; + gint data_fd; + + g_assert (SYSPROF_IS_KERNEL_SYMBOL_RESOLVER (self)); + g_assert (reader != NULL); + + if (-1 == (data_fd = sysprof_memfd_create ("[sysprof-kallsyms]")) || + !sysprof_capture_reader_read_file_fd (reader, "/proc/kallsyms", data_fd)) + { + if (data_fd != -1) + close (data_fd); + self->symbols = _sysprof_kernel_symbols_ref_shared (); + return; + } + + bytes = g_byte_array_new (); + lseek (data_fd, 0, SEEK_SET); + + for (;;) + { + gssize len = read (data_fd, buf, sizeof buf); + + if (len <= 0) + break; + + g_byte_array_append (bytes, buf, len); + } + + g_byte_array_append (bytes, zero, 1); + + if (bytes->len > 1) + { + kallsyms = sysprof_kallsyms_new_take ((gchar *)g_byte_array_free (g_steal_pointer (&bytes), FALSE)); + self->symbols = _sysprof_kernel_symbols_new_from_kallsyms (kallsyms); + } + else + { + self->symbols = _sysprof_kernel_symbols_ref_shared (); + } +} + static void symbol_resolver_iface_init (SysprofSymbolResolverInterface *iface) { + iface->load = sysprof_kernel_symbol_resolver_load; iface->resolve_with_context = sysprof_kernel_symbol_resolver_resolve_with_context; } @@ -68,9 +130,23 @@ G_DEFINE_TYPE_WITH_CODE (SysprofKernelSymbolResolver, G_IMPLEMENT_INTERFACE (SYSPROF_TYPE_SYMBOL_RESOLVER, symbol_resolver_iface_init)) +static void +sysprof_kernel_symbol_resolver_finalize (GObject *object) +{ + SysprofKernelSymbolResolver *self = (SysprofKernelSymbolResolver *)object; + + g_clear_pointer (&self->symbols, g_array_unref); + + G_OBJECT_CLASS (sysprof_kernel_symbol_resolver_parent_class)->finalize (object); +} + static void sysprof_kernel_symbol_resolver_class_init (SysprofKernelSymbolResolverClass *klass) { + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = sysprof_kernel_symbol_resolver_finalize; + linux_quark = g_quark_from_static_string ("Kernel"); } diff --git a/src/libsysprof/sysprof-kernel-symbol.c b/src/libsysprof/sysprof-kernel-symbol.c index 7c75d5db..3213c8be 100644 --- a/src/libsysprof/sysprof-kernel-symbol.c +++ b/src/libsysprof/sysprof-kernel-symbol.c @@ -28,11 +28,12 @@ #include "sysprof-helpers.h" #include "sysprof-kallsyms.h" #include "sysprof-kernel-symbol.h" +#include "sysprof-private.h" -static GArray *kernel_symbols; +static G_LOCK_DEFINE (kernel_lock); static GStringChunk *kernel_symbol_strs; -static GHashTable *kernel_symbols_skip_hash; -static const gchar *kernel_symbols_skip[] = { +static GHashTable *kernel_symbols_skip_hash; +static const gchar *kernel_symbols_skip[] = { /* IRQ stack */ "common_interrupt", "apic_timer_interrupt", @@ -65,6 +66,13 @@ static const gchar *kernel_symbols_skip[] = { "perf_callchain", }; +static inline gboolean +type_is_ignored (guint8 type) +{ + /* Only allow symbols in the text (code) section */ + return (type != 't' && type != 'T'); +} + static gint sysprof_kernel_symbol_compare (gconstpointer a, gconstpointer b) @@ -80,37 +88,41 @@ sysprof_kernel_symbol_compare (gconstpointer a, return -1; } -static inline gboolean -type_is_ignored (guint8 type) +static void +do_shared_init (void) { - /* Only allow symbols in the text (code) section */ - return (type != 't' && type != 'T'); + static gsize once; + + if (g_once_init_enter (&once)) + { + g_autoptr(GHashTable) skip = NULL; + + kernel_symbol_strs = g_string_chunk_new (4096 * 4); + + skip = g_hash_table_new (g_str_hash, g_str_equal); + for (guint i = 0; i < G_N_ELEMENTS (kernel_symbols_skip); i++) + g_hash_table_insert (skip, (gchar *)kernel_symbols_skip[i], NULL); + kernel_symbols_skip_hash = g_steal_pointer (&skip); + + g_once_init_leave (&once, TRUE); + } } -static gboolean -sysprof_kernel_symbol_load (void) +SysprofKernelSymbols * +_sysprof_kernel_symbols_new_from_kallsyms (SysprofKallsyms *kallsyms) { - SysprofHelpers *helpers = sysprof_helpers_get_default (); - g_autoptr(SysprofKallsyms) kallsyms = NULL; - g_autoptr(GHashTable) skip = NULL; - g_autoptr(GArray) ar = NULL; - g_autofree gchar *contents = NULL; + SysprofKernelSymbols *self; const gchar *name; guint64 addr; guint8 type; - skip = g_hash_table_new (g_str_hash, g_str_equal); - for (guint i = 0; i < G_N_ELEMENTS (kernel_symbols_skip); i++) - g_hash_table_insert (skip, (gchar *)kernel_symbols_skip[i], NULL); - kernel_symbols_skip_hash = g_steal_pointer (&skip); + do_shared_init (); - if (!sysprof_helpers_get_proc_file (helpers, "/proc/kallsyms", NULL, &contents, NULL)) - return FALSE; + g_return_val_if_fail (kallsyms != NULL, NULL); - kernel_symbol_strs = g_string_chunk_new (4096 * 4); - kallsyms = sysprof_kallsyms_new_take (g_steal_pointer (&contents)); - ar = g_array_new (FALSE, FALSE, sizeof (SysprofKernelSymbol)); + self = g_array_new (FALSE, FALSE, sizeof (SysprofKernelSymbol)); + G_LOCK (kernel_lock); while (sysprof_kallsyms_next (kallsyms, &name, &addr, &type)) { if (!type_is_ignored (type)) @@ -120,25 +132,34 @@ sysprof_kernel_symbol_load (void) sym.address = addr; sym.name = g_string_chunk_insert_const (kernel_symbol_strs, name); - g_array_append_val (ar, sym); + g_array_append_val (self, sym); + } + } + G_UNLOCK (kernel_lock); + + g_array_sort (self, sysprof_kernel_symbol_compare); + + return g_steal_pointer (&self); +} + +SysprofKernelSymbols * +_sysprof_kernel_symbols_ref_shared (void) +{ + static SysprofKernelSymbols *shared; + + if (shared == NULL) + { + SysprofHelpers *helpers = sysprof_helpers_get_default (); + g_autofree gchar *contents = NULL; + + if (sysprof_helpers_get_proc_file (helpers, "/proc/kallsyms", NULL, &contents, NULL)) + { + g_autoptr(SysprofKallsyms) kallsyms = sysprof_kallsyms_new_take (g_steal_pointer (&contents)); + shared = _sysprof_kernel_symbols_new_from_kallsyms (kallsyms); } } - g_array_sort (ar, sysprof_kernel_symbol_compare); - -#if 0 - g_print ("First: 0x%lx Last: 0x%lx\n", - g_array_index (ar, SysprofKernelSymbol, 0).address, - g_array_index (ar, SysprofKernelSymbol, ar->len - 1).address); -#endif - - if (ar->len > 0) - { - kernel_symbols = g_steal_pointer (&ar); - return TRUE; - } - - return FALSE; + return g_array_ref (shared); } static const SysprofKernelSymbol * @@ -174,8 +195,9 @@ sysprof_kernel_symbol_lookup (SysprofKernelSymbol *symbols, } } -/** - * sysprof_kernel_symbol_from_address: +/* + * sysprof_kernel_symbols_lookup: + * @self: the symbol data to lookup * @address: the address of the instruction pointer * * Locates the kernel symbol that contains @address. @@ -183,37 +205,26 @@ sysprof_kernel_symbol_lookup (SysprofKernelSymbol *symbols, * Returns: (transfer none): An #SysprofKernelSymbol or %NULL. */ const SysprofKernelSymbol * -sysprof_kernel_symbol_from_address (SysprofCaptureAddress address) +_sysprof_kernel_symbols_lookup (const SysprofKernelSymbols *self, + SysprofCaptureAddress address) { const SysprofKernelSymbol *first; const SysprofKernelSymbol *ret; - if G_UNLIKELY (kernel_symbols == NULL) - { - static gboolean failed; + g_assert (self != NULL); - if (failed) - return NULL; - - if (!sysprof_kernel_symbol_load ()) - { - failed = TRUE; - return NULL; - } - } - - g_assert (kernel_symbols != NULL); - g_assert (kernel_symbols->len > 0); + if (self->len == 0) + return NULL; /* Short circuit if this is out of range */ - first = &g_array_index (kernel_symbols, SysprofKernelSymbol, 0); + first = &g_array_index (self, SysprofKernelSymbol, 0); if (address < first->address) return NULL; - ret = sysprof_kernel_symbol_lookup ((SysprofKernelSymbol *)(gpointer)kernel_symbols->data, + ret = sysprof_kernel_symbol_lookup ((SysprofKernelSymbol *)(gpointer)self->data, address, 0, - kernel_symbols->len - 1); + self->len - 1); /* We resolve all symbols, including ignored symbols so that we * don't give back the wrong function juxtapose an ignored func. diff --git a/src/libsysprof/sysprof-kernel-symbol.h b/src/libsysprof/sysprof-kernel-symbol.h index af9e7ca1..7ef72f01 100644 --- a/src/libsysprof/sysprof-kernel-symbol.h +++ b/src/libsysprof/sysprof-kernel-symbol.h @@ -31,10 +31,7 @@ G_BEGIN_DECLS typedef struct { SysprofCaptureAddress address; - const gchar *name; + const gchar *name; } SysprofKernelSymbol; -SYSPROF_AVAILABLE_IN_ALL -const SysprofKernelSymbol *sysprof_kernel_symbol_from_address (SysprofCaptureAddress address); - G_END_DECLS diff --git a/src/libsysprof/sysprof-private.h b/src/libsysprof/sysprof-private.h new file mode 100644 index 00000000..83eabe47 --- /dev/null +++ b/src/libsysprof/sysprof-private.h @@ -0,0 +1,39 @@ +/* sysprof-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.h" +#include "sysprof-kallsyms.h" + +G_BEGIN_DECLS + +typedef GArray SysprofKernelSymbols; + +SysprofKernelSymbols *_sysprof_kernel_symbols_ref_shared (void); +SysprofKernelSymbols *_sysprof_kernel_symbols_new_from_kallsyms (SysprofKallsyms *kallsyms); +const SysprofKernelSymbol *_sysprof_kernel_symbols_lookup (const SysprofKernelSymbols *self, + SysprofCaptureAddress address); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofKernelSymbols, g_array_unref) + +G_END_DECLS diff --git a/src/libsysprof/sysprof.h b/src/libsysprof/sysprof.h index a88f47ad..2a73501f 100644 --- a/src/libsysprof/sysprof.h +++ b/src/libsysprof/sysprof.h @@ -27,7 +27,6 @@ G_BEGIN_DECLS # include "sysprof-callgraph-profile.h" # include "sysprof-capture-gobject.h" # include "sysprof-local-profiler.h" -# include "sysprof-kallsyms.h" # include "sysprof-profile.h" # include "sysprof-profiler.h" # include "sysprof-map-lookaside.h" diff --git a/src/tests/meson.build b/src/tests/meson.build index a519283f..6d3cf640 100644 --- a/src/tests/meson.build +++ b/src/tests/meson.build @@ -27,12 +27,6 @@ test_capture_cursor = executable('test-capture-cursor', 'test-capture-cursor.c', test('test-capture', test_capture, env: test_env) test('test-capture-cursor', test_capture_cursor, env: test_env) -# Use ./tests/test-kallsyms /proc/kallsyms to test (as user or root) -test_kallsyms = executable('test-kallsyms', 'test-kallsyms.c', - c_args: test_cflags, - dependencies: test_deps, -) - if get_option('enable_gtk') test_ui_deps = [ diff --git a/src/tests/test-kallsyms.c b/src/tests/test-kallsyms.c index c7ef0bc7..4b8dff50 100644 --- a/src/tests/test-kallsyms.c +++ b/src/tests/test-kallsyms.c @@ -2,6 +2,8 @@ #include #include +#include "sysprof-kallsyms.h" + int main (gint argc, gchar *argv[])