/* sysprof-elf-symbol-resolver.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 #include "binfile.h" #include "elfparser.h" #include "sysprof-elf-symbol-resolver.h" #include "sysprof-map-lookaside.h" struct _SysprofElfSymbolResolver { GObject parent_instance; GHashTable *lookasides; GHashTable *bin_files; GHashTable *tag_cache; }; static void symbol_resolver_iface_init (SysprofSymbolResolverInterface *iface); G_DEFINE_TYPE_EXTENDED (SysprofElfSymbolResolver, sysprof_elf_symbol_resolver, G_TYPE_OBJECT, 0, G_IMPLEMENT_INTERFACE (SYSPROF_TYPE_SYMBOL_RESOLVER, symbol_resolver_iface_init)) static void sysprof_elf_symbol_resolver_finalize (GObject *object) { SysprofElfSymbolResolver *self = (SysprofElfSymbolResolver *)object; g_clear_pointer (&self->bin_files, g_hash_table_unref); g_clear_pointer (&self->lookasides, g_hash_table_unref); g_clear_pointer (&self->tag_cache, g_hash_table_unref); G_OBJECT_CLASS (sysprof_elf_symbol_resolver_parent_class)->finalize (object); } static void sysprof_elf_symbol_resolver_class_init (SysprofElfSymbolResolverClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = sysprof_elf_symbol_resolver_finalize; } static void sysprof_elf_symbol_resolver_init (SysprofElfSymbolResolver *self) { self->lookasides = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)sysprof_map_lookaside_free); 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); } static void sysprof_elf_symbol_resolver_load (SysprofSymbolResolver *resolver, SysprofCaptureReader *reader) { SysprofElfSymbolResolver *self = (SysprofElfSymbolResolver *)resolver; SysprofCaptureFrameType type; g_assert (SYSPROF_IS_SYMBOL_RESOLVER (resolver)); g_assert (reader != NULL); sysprof_capture_reader_reset (reader); while (sysprof_capture_reader_peek_type (reader, &type)) { const SysprofCaptureMap *ev; SysprofMapLookaside *lookaside; SysprofMap map; if (type != SYSPROF_CAPTURE_FRAME_MAP) { if (!sysprof_capture_reader_skip (reader)) return; continue; } ev = sysprof_capture_reader_read_map (reader); map.start = ev->start; map.end = ev->end; map.offset = ev->offset; map.inode = ev->inode; map.filename = ev->filename; lookaside = g_hash_table_lookup (self->lookasides, GINT_TO_POINTER (ev->frame.pid)); if (lookaside == NULL) { lookaside = sysprof_map_lookaside_new (); g_hash_table_insert (self->lookasides, GINT_TO_POINTER (ev->frame.pid), lookaside); } sysprof_map_lookaside_insert (lookaside, &map); } } static bin_file_t * sysprof_elf_symbol_resolver_get_bin_file (SysprofElfSymbolResolver *self, const gchar *filename) { 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 == NULL) { const gchar *alternate = filename; /* * If we are in a new mount namespace, then rely on the sysprof_symbol_dirs * to find us a locate to resolve the file where the CRC will match. * * TODO: We need to translate the path here so that we can locate the * binary behind it (which then has links to the debug file in * the section header). */ if (g_str_has_prefix (filename, "/newroot/")) alternate += strlen ("/newroot"); bin_file = bin_file_new (alternate); g_hash_table_insert (self->bin_files, g_strdup (filename), bin_file); } return bin_file; } static GQuark guess_tag (SysprofElfSymbolResolver *self, const SysprofMap *map) { g_assert (map != NULL); g_assert (map->filename != NULL); if (!g_hash_table_contains (self->tag_cache, map->filename)) { GQuark tag = 0; if (strstr (map->filename, "/libgobject-2.0.")) tag = g_quark_from_static_string ("GObject"); else if (strstr (map->filename, "/libglib-2.0.")) tag = g_quark_from_static_string ("GLib"); else if (strstr (map->filename, "/libgio-2.0.")) tag = g_quark_from_static_string ("Gio"); else if (strstr (map->filename, "/libgirepository-1.0.")) tag = g_quark_from_static_string ("Introspection"); else if (strstr (map->filename, "/libgtk-3.")) tag = g_quark_from_static_string ("Gtk+"); else if (strstr (map->filename, "/libgdk-3.")) tag = g_quark_from_static_string ("Gdk"); else if (strstr (map->filename, "/libgtksourceview-3.0")) tag = g_quark_from_static_string ("GtkSourceView"); else if (strstr (map->filename, "/libpixman-1")) tag = g_quark_from_static_string ("Pixman"); else if (strstr (map->filename, "/libcairo.")) tag = g_quark_from_static_string ("cairo"); else if (strstr (map->filename, "/libgstreamer-1.")) tag = g_quark_from_static_string ("GStreamer"); else if (strstr (map->filename, "/libX11.")) tag = g_quark_from_static_string ("X11"); else if (strstr (map->filename, "/libpango-1.0.")) tag = g_quark_from_static_string ("Pango"); else if (strstr (map->filename, "/libpangocairo-1.0.")) tag = g_quark_from_static_string ("Pango"); else if (strstr (map->filename, "/libpangomm-1.4.")) tag = g_quark_from_static_string ("Pango"); else if (strstr (map->filename, "/libpangoft2-1.0")) tag = g_quark_from_static_string ("Pango"); else if (strstr (map->filename, "/libpangoxft-1.0.")) tag = g_quark_from_static_string ("Pango"); else if (strstr (map->filename, "/libclutter-")) tag = g_quark_from_static_string ("Clutter"); else if (strstr (map->filename, "/libcogl.") || strstr (map->filename, "/libcogl-")) tag = g_quark_from_static_string ("Cogl"); else if (strstr (map->filename, "/libffi.")) tag = g_quark_from_static_string ("libffi"); else if (strstr (map->filename, "/libwayland-")) tag = g_quark_from_static_string ("Wayland"); else if (strstr (map->filename, "/libinput.")) tag = g_quark_from_static_string ("libinput"); else if (strstr (map->filename, "/libgjs.")) tag = g_quark_from_static_string ("Gjs"); else if (strstr (map->filename, "/libmozjs-")) tag = g_quark_from_static_string ("MozJS"); else if (strstr (map->filename, "/libGL.")) tag = g_quark_from_static_string ("GL"); else if (strstr (map->filename, "/libEGL.")) tag = g_quark_from_static_string ("EGL"); g_hash_table_insert (self->tag_cache, g_strdup (map->filename), GSIZE_TO_POINTER (tag)); } return GPOINTER_TO_SIZE (g_hash_table_lookup (self->tag_cache, map->filename)); } static gchar * sysprof_elf_symbol_resolver_resolve_with_context (SysprofSymbolResolver *resolver, guint64 time, GPid pid, SysprofAddressContext context, SysprofCaptureAddress address, GQuark *tag) { SysprofElfSymbolResolver *self = (SysprofElfSymbolResolver *)resolver; const bin_symbol_t *bin_sym; SysprofMapLookaside *lookaside; const gchar *bin_sym_name; const SysprofMap *map; bin_file_t *bin_file; g_assert (SYSPROF_IS_ELF_SYMBOL_RESOLVER (self)); if (context != SYSPROF_ADDRESS_CONTEXT_USER) return NULL; lookaside = g_hash_table_lookup (self->lookasides, GINT_TO_POINTER (pid)); if (lookaside == NULL) return NULL; map = sysprof_map_lookaside_lookup (lookaside, address); if (map == NULL) return NULL; address -= map->start; address += map->offset; bin_file = sysprof_elf_symbol_resolver_get_bin_file (self, map->filename); g_assert (bin_file != NULL); if (map->inode && !bin_file_check_inode (bin_file, map->inode)) return g_strdup_printf ("%s: inode mismatch", map->filename); bin_sym = bin_file_lookup_symbol (bin_file, address); bin_sym_name = bin_symbol_get_name (bin_file, bin_sym); if (map->filename) *tag = guess_tag (self, map); return elf_demangle (bin_sym_name); } static void symbol_resolver_iface_init (SysprofSymbolResolverInterface *iface) { iface->load = sysprof_elf_symbol_resolver_load; iface->resolve_with_context = sysprof_elf_symbol_resolver_resolve_with_context; } SysprofSymbolResolver * sysprof_elf_symbol_resolver_new (void) { return g_object_new (SYSPROF_TYPE_ELF_SYMBOL_RESOLVER, NULL); }