mirror of
https://github.com/varun-r-mallya/sysprof.git
synced 2025-12-31 20:36:25 +00:00
callgraph: teach callgraph to use alternative symbol roots
This allows us to have a sysroot different than /, so that we can resolve symbols that are not necessarily installed on the system. For example, if we are running an application in an alternate mount namespace, we will want to resolve the symbols starting from the location of the checkout for that namespace. Generally alternate mount namespaces will have paths like "/newroot/usr/.." so by setting the source to "/newroot/usr/" to "~/.local/" you can do some fancy remapping.
This commit is contained in:
@ -57,16 +57,18 @@ struct _SpCallgraphProfile
|
||||
GObject parent_instance;
|
||||
|
||||
SpCaptureReader *reader;
|
||||
SpSelection *selection;
|
||||
SpSelection *selection;
|
||||
StackStash *stash;
|
||||
GStringChunk *symbols;
|
||||
GHashTable *symbol_dirs;
|
||||
GHashTable *tags;
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
SpCaptureReader *reader;
|
||||
SpSelection *selection;
|
||||
SpCaptureReader *reader;
|
||||
SpSelection *selection;
|
||||
GHashTable *symbol_dirs;
|
||||
} Generate;
|
||||
|
||||
static void profile_iface_init (SpProfileInterface *iface);
|
||||
@ -82,6 +84,20 @@ enum {
|
||||
|
||||
static GParamSpec *properties [N_PROPS];
|
||||
|
||||
static GHashTable *
|
||||
ht_copy (GHashTable *ht)
|
||||
{
|
||||
GHashTable *copy = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
|
||||
GHashTableIter iter;
|
||||
gpointer k,v;
|
||||
|
||||
g_hash_table_iter_init (&iter, ht);
|
||||
while (g_hash_table_iter_next (&iter, &k, &v))
|
||||
g_hash_table_insert (copy, g_strdup (k), g_strdup (v));
|
||||
|
||||
return ht;
|
||||
}
|
||||
|
||||
SpProfile *
|
||||
sp_callgraph_profile_new (void)
|
||||
{
|
||||
@ -104,6 +120,7 @@ sp_callgraph_profile_finalize (GObject *object)
|
||||
g_clear_pointer (&self->symbols, g_string_chunk_free);
|
||||
g_clear_pointer (&self->stash, stack_stash_unref);
|
||||
g_clear_pointer (&self->reader, sp_capture_reader_unref);
|
||||
g_clear_pointer (&self->symbol_dirs, g_hash_table_unref);
|
||||
g_clear_pointer (&self->tags, g_hash_table_unref);
|
||||
g_clear_object (&self->selection);
|
||||
|
||||
@ -172,6 +189,7 @@ sp_callgraph_profile_init (SpCallgraphProfile *self)
|
||||
{
|
||||
self->symbols = g_string_chunk_new (getpagesize ());
|
||||
self->tags = g_hash_table_new (g_str_hash, g_str_equal);
|
||||
self->symbol_dirs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -225,6 +243,7 @@ sp_callgraph_profile_generate_worker (GTask *task,
|
||||
g_autoptr(GHashTable) maps_by_pid = NULL;
|
||||
g_autoptr(GHashTable) cmdlines = NULL;
|
||||
g_autoptr(GPtrArray) resolvers = NULL;
|
||||
g_autoptr(SpSymbolResolver) elf_resolver = NULL;
|
||||
SpCaptureFrameType type;
|
||||
StackStash *stash = NULL;
|
||||
StackStash *resolved_stash = NULL;
|
||||
@ -245,9 +264,17 @@ sp_callgraph_profile_generate_worker (GTask *task,
|
||||
stash = stack_stash_new (NULL);
|
||||
resolved_stash = stack_stash_new (NULL);
|
||||
|
||||
/*
|
||||
* If we are running inside a mount namespace, such as with flatpak, we might
|
||||
* need to alter the sysroot to the checkout location of the container. That
|
||||
* way we can resolve elf files from inside the mount namespace.
|
||||
*/
|
||||
elf_resolver = sp_elf_symbol_resolver_new ();
|
||||
sp_elf_symbol_resolver_set_symbol_dirs (SP_ELF_SYMBOL_RESOLVER (elf_resolver), gen->symbol_dirs);
|
||||
|
||||
resolvers = g_ptr_array_new_with_free_func (g_object_unref);
|
||||
g_ptr_array_add (resolvers, sp_kernel_symbol_resolver_new ());
|
||||
g_ptr_array_add (resolvers, sp_elf_symbol_resolver_new ());
|
||||
g_ptr_array_add (resolvers, g_steal_pointer (&elf_resolver));
|
||||
g_ptr_array_add (resolvers, sp_jitmap_symbol_resolver_new ());
|
||||
|
||||
for (j = 0; j < resolvers->len; j++)
|
||||
@ -421,11 +448,12 @@ cleanup:
|
||||
}
|
||||
|
||||
static void
|
||||
generate_free (Generate *generate)
|
||||
generate_free (Generate *gen)
|
||||
{
|
||||
sp_capture_reader_unref (generate->reader);
|
||||
g_clear_object (&generate->selection);
|
||||
g_slice_free (Generate, generate);
|
||||
g_clear_pointer (&gen->reader, sp_capture_reader_unref);
|
||||
g_clear_pointer (&gen->symbol_dirs, g_hash_table_unref);
|
||||
g_clear_object (&gen->selection);
|
||||
g_slice_free (Generate, gen);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -445,6 +473,7 @@ sp_callgraph_profile_generate (SpProfile *profile,
|
||||
gen = g_slice_new0 (Generate);
|
||||
gen->reader = sp_capture_reader_copy (self->reader);
|
||||
gen->selection = sp_selection_copy (self->selection);
|
||||
gen->symbol_dirs = ht_copy (self->symbol_dirs);
|
||||
|
||||
task = g_task_new (self, cancellable, callback, user_data);
|
||||
g_task_set_task_data (task, gen, (GDestroyNotify)generate_free);
|
||||
@ -496,6 +525,16 @@ sp_callgraph_profile_get_stash (SpCallgraphProfile *self)
|
||||
return self->stash;
|
||||
}
|
||||
|
||||
void
|
||||
sp_callgraph_profile_add_symbol_dir (SpCallgraphProfile *self,
|
||||
const gchar *path,
|
||||
const gchar *symbol_dir)
|
||||
{
|
||||
g_return_if_fail (SP_IS_CALLGRAPH_PROFILE (self));
|
||||
|
||||
g_hash_table_insert (self->symbol_dirs, g_strdup (path), g_strdup (symbol_dir));
|
||||
}
|
||||
|
||||
GQuark
|
||||
sp_callgraph_profile_get_tag (SpCallgraphProfile *self,
|
||||
const gchar *symbol)
|
||||
|
||||
@ -30,6 +30,9 @@ G_DECLARE_FINAL_TYPE (SpCallgraphProfile, sp_callgraph_profile, SP, CALLGRAPH_PR
|
||||
|
||||
SpProfile *sp_callgraph_profile_new (void);
|
||||
SpProfile *sp_callgraph_profile_new_with_selection (SpSelection *selection);
|
||||
void sp_callgraph_profile_add_symbol_dir (SpCallgraphProfile *selection,
|
||||
const gchar *path,
|
||||
const gchar *symbol_dir);
|
||||
GQuark sp_callgraph_profile_get_tag (SpCallgraphProfile *self,
|
||||
const gchar *symbol);
|
||||
|
||||
|
||||
@ -24,6 +24,13 @@
|
||||
#include "sp-map-lookaside.h"
|
||||
#include "sp-elf-symbol-resolver.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
gchar *src;
|
||||
gchar *dst;
|
||||
gsize srclen;
|
||||
} SymbolDir;
|
||||
|
||||
struct _SpElfSymbolResolver
|
||||
{
|
||||
GObject parent_instance;
|
||||
@ -31,6 +38,7 @@ struct _SpElfSymbolResolver
|
||||
GHashTable *lookasides;
|
||||
GHashTable *bin_files;
|
||||
GHashTable *tag_cache;
|
||||
GArray *symbol_dirs;
|
||||
};
|
||||
|
||||
static void symbol_resolver_iface_init (SpSymbolResolverInterface *iface);
|
||||
@ -50,6 +58,7 @@ sp_elf_symbol_resolver_finalize (GObject *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_clear_pointer (&self->symbol_dirs, g_array_unref);
|
||||
|
||||
G_OBJECT_CLASS (sp_elf_symbol_resolver_parent_class)->finalize (object);
|
||||
}
|
||||
@ -65,6 +74,8 @@ sp_elf_symbol_resolver_class_init (SpElfSymbolResolverClass *klass)
|
||||
static void
|
||||
sp_elf_symbol_resolver_init (SpElfSymbolResolver *self)
|
||||
{
|
||||
self->symbol_dirs = g_array_new (FALSE, FALSE, sizeof (SymbolDir));
|
||||
|
||||
self->lookasides = g_hash_table_new_full (NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
@ -131,11 +142,30 @@ sp_elf_symbol_resolver_get_bin_file (SpElfSymbolResolver *self,
|
||||
|
||||
g_assert (SP_IS_ELF_SYMBOL_RESOLVER (self));
|
||||
|
||||
if (filename == NULL)
|
||||
return NULL;
|
||||
|
||||
bin_file = g_hash_table_lookup (self->bin_files, filename);
|
||||
|
||||
if (bin_file == NULL)
|
||||
{
|
||||
bin_file = bin_file_new (filename);
|
||||
g_autofree gchar *translated = NULL;
|
||||
const gchar *alternate = filename;
|
||||
|
||||
/*
|
||||
* See if we have a symbol directory that is set to be the resolution
|
||||
* path for this filename. We will want to alter where we find the
|
||||
* symbols based on this directory.
|
||||
*/
|
||||
for (guint i = 0; i < self->symbol_dirs->len; i++)
|
||||
{
|
||||
const SymbolDir *sd = &g_array_index (self->symbol_dirs, SymbolDir, i);
|
||||
|
||||
if (g_str_has_prefix (filename, sd->src))
|
||||
alternate = translated = g_build_filename (sd->dst, filename + sd->srclen, NULL);
|
||||
}
|
||||
|
||||
bin_file = bin_file_new (alternate);
|
||||
g_hash_table_insert (self->bin_files, g_strdup (filename), bin_file);
|
||||
}
|
||||
|
||||
@ -292,3 +322,33 @@ sp_elf_symbol_resolver_new (void)
|
||||
{
|
||||
return g_object_new (SP_TYPE_ELF_SYMBOL_RESOLVER, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
sp_elf_symbol_resolver_set_symbol_dirs (SpElfSymbolResolver *self,
|
||||
GHashTable *symbol_dirs)
|
||||
{
|
||||
GHashTableIter iter;
|
||||
|
||||
g_return_if_fail (SP_IS_ELF_SYMBOL_RESOLVER (self));
|
||||
|
||||
if (self->symbol_dirs->len)
|
||||
g_array_remove_range (self->symbol_dirs, 0, self->symbol_dirs->len - 1);
|
||||
|
||||
if (symbol_dirs)
|
||||
{
|
||||
gpointer k, v;
|
||||
|
||||
g_hash_table_iter_init (&iter, symbol_dirs);
|
||||
|
||||
while (g_hash_table_iter_next (&iter, &k, &v))
|
||||
{
|
||||
SymbolDir dir;
|
||||
|
||||
dir.src = g_strdup ((gchar *)k);
|
||||
dir.srclen = strlen (dir.src);
|
||||
dir.dst = g_strdup ((gchar *)v);
|
||||
|
||||
g_array_append_val (self->symbol_dirs, dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,7 +27,9 @@ G_BEGIN_DECLS
|
||||
|
||||
G_DECLARE_FINAL_TYPE (SpElfSymbolResolver, sp_elf_symbol_resolver, SP, ELF_SYMBOL_RESOLVER, GObject)
|
||||
|
||||
SpSymbolResolver *sp_elf_symbol_resolver_new (void);
|
||||
SpSymbolResolver *sp_elf_symbol_resolver_new (void);
|
||||
void sp_elf_symbol_resolver_set_symbol_dirs (SpElfSymbolResolver *self,
|
||||
GHashTable *symbol_dirs);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
||||
Reference in New Issue
Block a user