diff --git a/lib/sp-callgraph-profile.c b/lib/sp-callgraph-profile.c index 57b09394..a3fae6f1 100644 --- a/lib/sp-callgraph-profile.c +++ b/lib/sp-callgraph-profile.c @@ -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) diff --git a/lib/sp-callgraph-profile.h b/lib/sp-callgraph-profile.h index e034748f..916ac349 100644 --- a/lib/sp-callgraph-profile.h +++ b/lib/sp-callgraph-profile.h @@ -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); diff --git a/lib/sp-elf-symbol-resolver.c b/lib/sp-elf-symbol-resolver.c index 8d78d86d..507a6d3d 100644 --- a/lib/sp-elf-symbol-resolver.c +++ b/lib/sp-elf-symbol-resolver.c @@ -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); + } + } +} diff --git a/lib/sp-elf-symbol-resolver.h b/lib/sp-elf-symbol-resolver.h index 3b359af5..eb25b83d 100644 --- a/lib/sp-elf-symbol-resolver.h +++ b/lib/sp-elf-symbol-resolver.h @@ -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