Files
sysprof/lib/sp-elf-symbol-resolver.c
Christian Hergert e759f2999f elf: tag GtkSourceView symbols
We see this library enough in the platform that it is work tagging.
2016-04-20 20:36:01 -07:00

283 lines
8.4 KiB
C

/* sp-elf-symbol-resolver.c
*
* Copyright (C) 2016 Christian Hergert <chergert@redhat.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#include "binfile.h"
#include "elfparser.h"
#include "sp-map-lookaside.h"
#include "sp-elf-symbol-resolver.h"
struct _SpElfSymbolResolver
{
GObject parent_instance;
GHashTable *lookasides;
GHashTable *bin_files;
GHashTable *tag_cache;
};
static void symbol_resolver_iface_init (SpSymbolResolverInterface *iface);
G_DEFINE_TYPE_EXTENDED (SpElfSymbolResolver,
sp_elf_symbol_resolver,
G_TYPE_OBJECT,
0,
G_IMPLEMENT_INTERFACE (SP_TYPE_SYMBOL_RESOLVER,
symbol_resolver_iface_init))
static void
sp_elf_symbol_resolver_finalize (GObject *object)
{
SpElfSymbolResolver *self = (SpElfSymbolResolver *)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 (sp_elf_symbol_resolver_parent_class)->finalize (object);
}
static void
sp_elf_symbol_resolver_class_init (SpElfSymbolResolverClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = sp_elf_symbol_resolver_finalize;
}
static void
sp_elf_symbol_resolver_init (SpElfSymbolResolver *self)
{
self->lookasides = g_hash_table_new_full (NULL,
NULL,
NULL,
(GDestroyNotify)sp_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
sp_elf_symbol_resolver_load (SpSymbolResolver *resolver,
SpCaptureReader *reader)
{
SpElfSymbolResolver *self = (SpElfSymbolResolver *)resolver;
SpCaptureFrameType type;
g_assert (SP_IS_SYMBOL_RESOLVER (resolver));
g_assert (reader != NULL);
sp_capture_reader_reset (reader);
while (sp_capture_reader_peek_type (reader, &type))
{
const SpCaptureMap *ev;
SpMapLookaside *lookaside;
SpMap map;
if (type != SP_CAPTURE_FRAME_MAP)
{
if (!sp_capture_reader_skip (reader))
return;
continue;
}
ev = sp_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 = sp_map_lookaside_new ();
g_hash_table_insert (self->lookasides, GINT_TO_POINTER (ev->frame.pid), lookaside);
}
sp_map_lookaside_insert (lookaside, &map);
}
}
static bin_file_t *
sp_elf_symbol_resolver_get_bin_file (SpElfSymbolResolver *self,
const gchar *filename)
{
bin_file_t *bin_file;
g_assert (SP_IS_ELF_SYMBOL_RESOLVER (self));
bin_file = g_hash_table_lookup (self->bin_files, filename);
if (bin_file == NULL)
{
bin_file = bin_file_new (filename);
g_hash_table_insert (self->bin_files, g_strdup (filename), bin_file);
}
return bin_file;
}
static GQuark
guess_tag (SpElfSymbolResolver *self,
const SpMap *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, "/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 *
sp_elf_symbol_resolver_resolve (SpSymbolResolver *resolver,
guint64 time,
GPid pid,
SpCaptureAddress address,
GQuark *tag)
{
SpElfSymbolResolver *self = (SpElfSymbolResolver *)resolver;
const bin_symbol_t *bin_sym;
SpMapLookaside *lookaside;
const gchar *bin_sym_name;
const SpMap *map;
bin_file_t *bin_file;
g_assert (SP_IS_ELF_SYMBOL_RESOLVER (self));
lookaside = g_hash_table_lookup (self->lookasides, GINT_TO_POINTER (pid));
if (lookaside == NULL)
return NULL;
map = sp_map_lookaside_lookup (lookaside, address);
if (map == NULL)
return NULL;
address -= map->start;
address += map->offset;
bin_file = sp_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 (SpSymbolResolverInterface *iface)
{
iface->load = sp_elf_symbol_resolver_load;
iface->resolve = sp_elf_symbol_resolver_resolve;
}
SpSymbolResolver *
sp_elf_symbol_resolver_new (void)
{
return g_object_new (SP_TYPE_ELF_SYMBOL_RESOLVER, NULL);
}