diff --git a/src/libsysprof/binfile.c b/src/libsysprof/binfile.c index 145322ef..2cb74187 100644 --- a/src/libsysprof/binfile.c +++ b/src/libsysprof/binfile.c @@ -89,7 +89,8 @@ already_warned (const char *name) } static ElfParser * -get_build_id_file (ElfParser *elf) +get_build_id_file (ElfParser *elf, + const char * const *debug_dirs) { const char *build_id; GList *tries = NULL, *list; @@ -108,12 +109,14 @@ get_build_id_file (ElfParser *elf) init = g_strndup (build_id, 2); rest = g_strdup_printf ("%s%s", build_id + 2, ".debug"); - tmp = g_build_filename ( - "/usr", "lib", "debug", ".build-id", init, rest, NULL); - tries = g_list_append (tries, tmp); - - tmp = g_build_filename (DEBUGDIR, ".build-id", init, rest, NULL); - tries = g_list_append (tries, tmp); + if (debug_dirs) + { + for (guint i = 0; debug_dirs[i]; i++) + { + tmp = g_build_filename (debug_dirs[i], ".build-id", init, rest, NULL); + tries = g_list_append (tries, tmp); + } + } for (list = tries; list != NULL; list = list->next) { @@ -150,11 +153,10 @@ get_debuglink_file (ElfParser *elf, const gchar * const *debug_dirs) { const char *basename; - char *dir; guint32 crc32; ElfParser *result = NULL; const char *build_id; - guint i; + char *dir; if (!elf) return NULL; @@ -172,9 +174,9 @@ get_debuglink_file (ElfParser *elf, dir = g_path_get_dirname (filename); - for (i = 0; debug_dirs[i]; i++) + for (guint i = 0; debug_dirs[i]; i++) { - const char *name = debug_dirs[i]; + char *name = g_build_filename (debug_dirs[i], basename, NULL); ElfParser *parser = elf_parser_new (name, NULL); guint32 file_crc; const char *file_build_id; @@ -194,7 +196,7 @@ get_debuglink_file (ElfParser *elf, if (file_crc == crc32) { result = parser; - *new_name = g_strdup (name); + *new_name = g_steal_pointer (&name); break; } else @@ -209,6 +211,8 @@ get_debuglink_file (ElfParser *elf, skip: elf_parser_free (parser); } + + g_free (name); } g_free (dir); @@ -226,7 +230,7 @@ get_debug_binaries (GList *files, GHashTable *seen_names; GList *free_us = NULL; - build_id_file = get_build_id_file (elf); + build_id_file = get_build_id_file (elf, debug_dirs); if (build_id_file) return g_list_prepend (files, build_id_file); @@ -354,9 +358,6 @@ bin_file_new (const char *filename, ElfParser *elf = NULL; bin_file_t *bf; - if (g_str_has_prefix (filename, "/var/run/host/")) - real_filename = filename + strlen ("/var/run/host"); - bf = g_new0 (bin_file_t, 1); bf->inode_check = FALSE; @@ -475,9 +476,9 @@ bin_file_check_inode (bin_file_t *bin_file, } static const ElfSym * -get_elf_sym (bin_file_t *file, - const bin_symbol_t *symbol, - ElfParser **elf_ret) +get_elf_sym (bin_file_t *file, + const bin_symbol_t *symbol, + ElfParser **elf_ret) { GList *list; @@ -500,7 +501,7 @@ get_elf_sym (bin_file_t *file, } const char * -bin_symbol_get_name (bin_file_t *file, +bin_symbol_get_name (bin_file_t *file, const bin_symbol_t *symbol) { if (file->undefined_name == (char *)symbol) diff --git a/src/libsysprof/elfparser.c b/src/libsysprof/elfparser.c index 376a1f0e..ec427ae0 100644 --- a/src/libsysprof/elfparser.c +++ b/src/libsysprof/elfparser.c @@ -124,6 +124,28 @@ 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) { @@ -252,17 +274,31 @@ elf_parser_new_from_data (const guchar *data, return parser; } -ElfParser * -elf_parser_new (const char *filename, - GError **err) +static GMappedFile * +open_mapped_file (const char *filename, + GError **error) { + GMappedFile *file; + char *alternate = NULL; + + if (in_container () && !g_str_has_prefix (filename, g_get_home_dir ())) + filename = alternate = g_build_filename ("/var/run/host", filename, NULL); + file = g_mapped_file_new (filename, FALSE, error); + g_free (alternate); + + return file; +} + +ElfParser * +elf_parser_new (const char *filename, + GError **error) +{ + GMappedFile *file; const guchar *data; gsize length; ElfParser *parser; - GMappedFile *file = g_mapped_file_new (filename, FALSE, NULL); - - if (!file) + if (!(file = open_mapped_file (filename, error))) return NULL; #if 0 @@ -284,6 +320,11 @@ elf_parser_new (const char *filename, 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; } @@ -730,7 +771,8 @@ elf_parser_get_build_id (ElfParser *parser) } const char * -elf_parser_get_debug_link (ElfParser *parser, guint32 *crc32) +elf_parser_get_debug_link (ElfParser *parser, + guint32 *crc32) { guint64 offset; const Section *debug_link = find_section (parser, ".gnu_debuglink", @@ -820,7 +862,3 @@ elf_parser_get_sym_address_range (ElfParser *parser, *begin = sym->address - parser->text_section->load_address; *end = *begin + st_size (parser, sym->table, sym->offset); } - -/* - * Utility functions - */ diff --git a/src/libsysprof/sysprof-elf-symbol-resolver.c b/src/libsysprof/sysprof-elf-symbol-resolver.c index c555e056..b7fe059b 100644 --- a/src/libsysprof/sysprof-elf-symbol-resolver.c +++ b/src/libsysprof/sysprof-elf-symbol-resolver.c @@ -32,6 +32,13 @@ #include "sysprof-podman.h" #include "sysprof-symbol-resolver-private.h" +typedef enum +{ + PROCESS_KIND_STANDARD, + PROCESS_KIND_FLATPAK, + PROCESS_KIND_PODMAN, +} ProcessKind; + typedef struct { char *on_host; @@ -41,11 +48,14 @@ typedef struct typedef struct { - SysprofMapLookaside *lookaside; - SysprofPathResolver *resolver; - GByteArray *mountinfo_data; - GArray *overlays; - int pid; + SysprofMapLookaside *lookaside; + SysprofPathResolver *resolver; + GByteArray *mountinfo_data; + GArray *overlays; + char **debug_dirs; + char *info; + int pid; + guint kind : 2; } ProcessInfo; struct _SysprofElfSymbolResolver @@ -54,10 +64,8 @@ struct _SysprofElfSymbolResolver GHashTable *processes; GStringChunk *chunks; - - GArray *debug_dirs; - GHashTable *bin_files; - GHashTable *tag_cache; + GHashTable *bin_files; + GHashTable *tag_cache; }; static void symbol_resolver_iface_init (SysprofSymbolResolverInterface *iface); @@ -76,26 +84,27 @@ process_info_free (gpointer data) if (pi != NULL) { - g_clear_pointer (&pi->mountinfo_data, g_byte_array_unref); - g_clear_pointer (&pi->resolver, _sysprof_path_resolver_free); g_clear_pointer (&pi->lookaside, sysprof_map_lookaside_free); + g_clear_pointer (&pi->resolver, _sysprof_path_resolver_free); + g_clear_pointer (&pi->mountinfo_data, g_byte_array_unref); + g_clear_pointer (&pi->overlays, g_array_unref); + g_clear_pointer (&pi->debug_dirs, g_strfreev); + g_clear_pointer (&pi->info, g_free); g_slice_free (ProcessInfo, pi); } } -static gboolean -is_flatpak (void) +static const char * const * +process_info_get_debug_dirs (const ProcessInfo *pi) { - static gsize did_init = FALSE; - static gboolean ret; + static const char *standard[] = { "/usr/lib/debug", NULL }; - if (g_once_init_enter (&did_init)) - { - ret = g_file_test ("/.flatpak-info", G_FILE_TEST_EXISTS); - g_once_init_leave (&did_init, TRUE); - } - - return ret; + if (pi->kind == PROCESS_KIND_FLATPAK) + return standard; /* TODO */ + else if (pi->kind == PROCESS_KIND_PODMAN) + return standard; /* TODO */ + else + return standard; } static void @@ -105,8 +114,8 @@ sysprof_elf_symbol_resolver_finalize (GObject *object) g_clear_pointer (&self->bin_files, g_hash_table_unref); g_clear_pointer (&self->tag_cache, g_hash_table_unref); - g_clear_pointer (&self->debug_dirs, g_array_unref); g_clear_pointer (&self->processes, g_hash_table_unref); + g_clear_pointer (&self->chunks, g_string_chunk_free); G_OBJECT_CLASS (sysprof_elf_symbol_resolver_parent_class)->finalize (object); } @@ -119,44 +128,15 @@ sysprof_elf_symbol_resolver_class_init (SysprofElfSymbolResolverClass *klass) object_class->finalize = sysprof_elf_symbol_resolver_finalize; } -static void -free_element_string (gpointer data) -{ - g_free (*(gchar **)data); -} - static void sysprof_elf_symbol_resolver_init (SysprofElfSymbolResolver *self) { - g_auto(GStrv) podman_dirs = NULL; - + self->chunks = g_string_chunk_new (4096); self->processes = g_hash_table_new_full (NULL, NULL, NULL, process_info_free); - - self->debug_dirs = g_array_new (TRUE, FALSE, sizeof (gchar *)); - g_array_set_clear_func (self->debug_dirs, free_element_string); - - sysprof_elf_symbol_resolver_add_debug_dir (self, "/usr/lib/debug"); - sysprof_elf_symbol_resolver_add_debug_dir (self, "/usr/lib32/debug"); - sysprof_elf_symbol_resolver_add_debug_dir (self, "/usr/lib64/debug"); - - /* The user may have podman/toolbox containers */ - podman_dirs = sysprof_podman_debug_dirs (); - for (guint i = 0; podman_dirs[i]; i++) - sysprof_elf_symbol_resolver_add_debug_dir (self, podman_dirs[i]); - - if (is_flatpak ()) - { - g_auto(GStrv) debug_dirs = sysprof_flatpak_debug_dirs (); - - for (guint i = 0; debug_dirs[i]; i++) - sysprof_elf_symbol_resolver_add_debug_dir (self, debug_dirs[i]); - } - self->bin_files = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify)bin_file_free); - self->tag_cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); } @@ -188,6 +168,7 @@ sysprof_elf_symbol_resolver_load (SysprofSymbolResolver *resolver, g_autoptr(GByteArray) mounts = NULL; g_autofree char *mounts_data = NULL; GHashTableIter iter; + ProcessInfo *pi; gpointer k, v; g_assert (SYSPROF_IS_ELF_SYMBOL_RESOLVER (self)); @@ -206,17 +187,29 @@ sysprof_elf_symbol_resolver_load (SysprofSymbolResolver *resolver, if (type == SYSPROF_CAPTURE_FRAME_FILE_CHUNK) { const SysprofCaptureFileChunk *ev; - ProcessInfo *pi; int pid; if (!(ev = sysprof_capture_reader_read_file (reader))) break; - if (g_str_has_prefix (ev->path, "/proc/") && + pi = sysprof_elf_symbol_resolver_get_process (self, pid); + + if (strcmp (ev->path, "/.flatpak-info") == 0) + { + pi->kind = PROCESS_KIND_FLATPAK; + g_free (pi->info); + pi->info = g_strndup ((char *)ev->data, ev->len); + } + else if (strcmp (ev->path, "/run/.containerenv") == 0) + { + pi->kind = PROCESS_KIND_PODMAN; + g_free (pi->info); + pi->info = g_strndup ((char *)ev->data, ev->len); + } + else if (g_str_has_prefix (ev->path, "/proc/") && g_str_has_suffix (ev->path, "/mountinfo") && sscanf (ev->path, "/proc/%u/mountinfo", &pid) == 1) { - pi = sysprof_elf_symbol_resolver_get_process (self, pid); if (pi->mountinfo_data == NULL) pi->mountinfo_data = g_byte_array_new (); if (ev->len) @@ -234,7 +227,6 @@ sysprof_elf_symbol_resolver_load (SysprofSymbolResolver *resolver, { const SysprofCaptureOverlay *ev; ProcessOverlay ov; - ProcessInfo *pi; if (!(ev = sysprof_capture_reader_read_overlay (reader))) break; @@ -274,7 +266,7 @@ sysprof_elf_symbol_resolver_load (SysprofSymbolResolver *resolver, g_hash_table_iter_init (&iter, self->processes); while (g_hash_table_iter_next (&iter, &k, &v)) { - ProcessInfo *pi = v; + pi = v; if (pi->mountinfo_data == NULL) continue; @@ -292,6 +284,24 @@ sysprof_elf_symbol_resolver_load (SysprofSymbolResolver *resolver, _sysprof_path_resolver_add_overlay (pi->resolver, ov->in_process, ov->on_host, ov->layer); } } + + if (pi->kind == PROCESS_KIND_FLATPAK) + { + if (pi->info != NULL) + { + g_autoptr(GKeyFile) keyfile = g_key_file_new (); + + if (g_key_file_load_from_data (keyfile, pi->info, (gsize)-1, 0, NULL)) + { + } + } + } + else if (pi->kind == PROCESS_KIND_PODMAN) + { + pi->debug_dirs = g_new0 (gchar *, 2); + pi->debug_dirs[0] = _sysprof_path_resolver_resolve (pi->resolver, "/usr/lib/debug"); + pi->debug_dirs[1] = 0; + } } /* Walk through the file again and extract maps so long as @@ -305,7 +315,6 @@ sysprof_elf_symbol_resolver_load (SysprofSymbolResolver *resolver, const SysprofCaptureMap *ev = sysprof_capture_reader_read_map (reader); const char *filename = ev->filename; g_autofree char *resolved = NULL; - ProcessInfo *pi; SysprofMap map; pi = sysprof_elf_symbol_resolver_get_process (self, ev->frame.pid); @@ -339,44 +348,25 @@ sysprof_elf_symbol_resolver_load (SysprofSymbolResolver *resolver, static bin_file_t * sysprof_elf_symbol_resolver_get_bin_file (SysprofElfSymbolResolver *self, - const SysprofMapOverlay *overlays, - guint n_overlays, + const ProcessInfo *pi, const gchar *filename) { + g_autofree char *alternate = NULL; + const char * const *debug_dirs; + g_autofree char *on_host = NULL; bin_file_t *bin_file; g_assert (SYSPROF_IS_ELF_SYMBOL_RESOLVER (self)); - bin_file = g_hash_table_lookup (self->bin_files, filename); + if ((bin_file = g_hash_table_lookup (self->bin_files, filename))) + return bin_file; - if (bin_file == NULL) - { - g_autofree gchar *path = NULL; - const gchar *alternate = filename; - const gchar * const *dirs; - - dirs = (const gchar * const *)(gpointer)self->debug_dirs->data; - - if (overlays && filename[0] != '/' && filename[0] != '[') - { - for (guint i = 0; i < n_overlays; i++) - { - if (g_str_has_prefix (filename, overlays[i].dst+1)) - { - alternate = path = g_build_filename (overlays[i].src, filename, NULL); - break; - } - } - } - else if (is_flatpak () && g_str_has_prefix (filename, "/usr/")) - { - alternate = path = g_build_filename ("/var/run/host", alternate, NULL); - } - - bin_file = bin_file_new (alternate, dirs); - - g_hash_table_insert (self->bin_files, g_strdup (filename), bin_file); - } + /* Debug dirs are going to be dependent on the process as different + * containers may affect where the debug symbols are installed. + */ + debug_dirs = process_info_get_debug_dirs (pi); + bin_file = bin_file_new (filename, (const char * const *)debug_dirs); + g_hash_table_insert (self->bin_files, g_strdup (filename), bin_file); return bin_file; } @@ -527,8 +517,7 @@ sysprof_elf_symbol_resolver_resolve_full (SysprofElfSymbolResolver *self, address -= map->start; address += map->offset; - /* TODO: Get debugdirs for process */ - bin_file = sysprof_elf_symbol_resolver_get_bin_file (self, NULL, 0, map->filename); + bin_file = sysprof_elf_symbol_resolver_get_bin_file (self, pi, map->filename); g_assert (bin_file != NULL); @@ -602,22 +591,6 @@ void sysprof_elf_symbol_resolver_add_debug_dir (SysprofElfSymbolResolver *self, const gchar *debug_dir) { - gchar *val; - - g_return_if_fail (SYSPROF_IS_ELF_SYMBOL_RESOLVER (self)); - g_return_if_fail (debug_dir != NULL); - - if (!g_file_test (debug_dir, G_FILE_TEST_EXISTS)) - return; - - for (guint i = 0; i < self->debug_dirs->len; i++) - { - gchar * const *str = &g_array_index (self->debug_dirs, gchar *, i); - - if (g_strcmp0 (*str, debug_dir) == 0) - return; - } - - val = g_strdup (debug_dir); - g_array_append_val (self->debug_dirs, val); + /* Do Nothing */ + /* XXX: Mark as deprecated post 41 or remove with Gtk4 port */ } diff --git a/src/libsysprof/sysprof-proc-source.c b/src/libsysprof/sysprof-proc-source.c index 68dd3443..7edaee01 100644 --- a/src/libsysprof/sysprof-proc-source.c +++ b/src/libsysprof/sysprof-proc-source.c @@ -213,14 +213,16 @@ sysprof_proc_source_populate_overlays (SysprofProcSource *self, GPid pid, const char *cgroup) { + static GRegex *flatpak; static GRegex *podman; + g_autoptr(GMatchInfo) flatpak_match = NULL; g_autoptr(GMatchInfo) podman_match = NULL; g_assert (SYSPROF_IS_PROC_SOURCE (self)); g_assert (cgroup != NULL); - if (strcmp (cgroup, "")) + if (strcmp (cgroup, "") == 0) return; /* This function tries to discover the podman container that contains the @@ -241,10 +243,33 @@ sysprof_proc_source_populate_overlays (SysprofProcSource *self, g_assert (podman != NULL); } + if (flatpak == NULL) + { + flatpak = g_regex_new ("app-flatpak-([a-zA-Z_\\-\\.]+)-[0-9]+\\.scope", G_REGEX_OPTIMIZE, 0, NULL); + g_assert (flatpak != NULL); + } + if (g_regex_match (podman, cgroup, 0, &podman_match)) { + SysprofHelpers *helpers = sysprof_helpers_get_default (); 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_autofree char *info = NULL; + sysprof_proc_source_populate_pid_podman (self, pid, word); + + if (sysprof_helpers_get_proc_file (helpers, path, NULL, &info, NULL)) + add_file (self, pid, "/run/.containerenv", info); + } + else if (g_regex_match (flatpak, cgroup, 0, &flatpak_match)) + { + SysprofHelpers *helpers = sysprof_helpers_get_default (); + g_autofree char *word = g_match_info_fetch (flatpak_match, 1); + g_autofree char *path = g_strdup_printf ("/proc/%d/root/.flatpak-info", pid); + g_autofree char *info = NULL; + + if (sysprof_helpers_get_proc_file (helpers, path, NULL, &info, NULL)) + add_file (self, pid, "/.flatpak-info", info); } } diff --git a/src/libsysprof/sysprof-symbol-resolver-private.h b/src/libsysprof/sysprof-symbol-resolver-private.h index 76cbe45e..13039463 100644 --- a/src/libsysprof/sysprof-symbol-resolver-private.h +++ b/src/libsysprof/sysprof-symbol-resolver-private.h @@ -25,7 +25,8 @@ G_BEGIN_DECLS -char *_sysprof_symbol_resolver_load_file (SysprofCaptureReader *reader, - const char *path); +char *_sysprof_symbol_resolver_load_file (SysprofCaptureReader *reader, + const char *path); +char **_sysprof_symbol_resolver_guess_debug_dirs (const char *path); G_END_DECLS diff --git a/src/libsysprof/sysprof-symbol-resolver.c b/src/libsysprof/sysprof-symbol-resolver.c index 19e4140d..626a904b 100644 --- a/src/libsysprof/sysprof-symbol-resolver.c +++ b/src/libsysprof/sysprof-symbol-resolver.c @@ -185,3 +185,12 @@ _sysprof_symbol_resolver_load_file (SysprofCaptureReader *reader, return g_steal_pointer (&data); } + +char ** +_sysprof_symbol_resolver_guess_debug_dirs (const char *path) +{ + if (path == NULL) + return NULL; + + return NULL; +} diff --git a/src/tests/read-build-id.c b/src/tests/read-build-id.c index 804392c3..9f9a7ab0 100644 --- a/src/tests/read-build-id.c +++ b/src/tests/read-build-id.c @@ -32,6 +32,8 @@ main (int argc, const char *filename = argv[i]; ElfParser *parser = elf_parser_new (filename, &error); const char *build_id; + const char *debug_link; + guint crc; if (parser == NULL) { @@ -44,7 +46,10 @@ main (int argc, } build_id = elf_parser_get_build_id (parser); - g_print ("%s: %s\n", filename, build_id); + debug_link = elf_parser_get_debug_link (parser, &crc); + + g_print ("%s: %s (%s)\n", filename, build_id, debug_link); + elf_parser_free (parser); } diff --git a/src/tests/test-utils.c b/src/tests/test-utils.c new file mode 100644 index 00000000..4b27b70d --- /dev/null +++ b/src/tests/test-utils.c @@ -0,0 +1,49 @@ +/* test-utils.c + * + * 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 + */ + +#include + +static void +test_debug_dirs (void) +{ + g_auto(GStrv) test1 = NULL; + g_auto(GStrv) test2 = NULL; + + test1 = _sysprof_symbol_resolver_guess_debug_dirs ("/usr/bin/foo"); + g_assert_nonnull (test1); + g_assert_cmpstr (test1[0], ==, "/usr/lib/debug/bin"); + g_assert_cmpstr (test1[1], ==, "/usr/lib64/debug/bin"); + g_assert_cmpstr (test1[2], ==, NULL); + + test2 = _sysprof_symbol_resolver_guess_debug_dirs ("/usr/lib64/libc.so.6"); + g_assert_nonnull (test2); + g_assert_cmpstr (test2[0], ==, "/usr/lib/debug/lib64"); + g_assert_cmpstr (test2[1], ==, "/usr/lib64/debug/lib64"); + g_assert_cmpstr (test2[2], ==, NULL); +} + +int +main (int argc, + char *argv[]) +{ + g_test_init (&argc, &argv, NULL); + g_test_add_func ("/Sysprof/SymbolResolver/guess_debug_dirs", test_debug_dirs); + return g_test_run (); +}