[sysprof] callgraph: teach callgraph to use alternative symbol roots



commit 222d0ad53fcc35155270ece3f0a71d619e6a09e6
Author: Christian Hergert <chergert redhat com>
Date:   Sat Feb 25 19:34:58 2017 -0800

    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.

 lib/sp-callgraph-profile.c   |   55 +++++++++++++++++++++++++++++++-----
 lib/sp-callgraph-profile.h   |    3 ++
 lib/sp-elf-symbol-resolver.c |   62 +++++++++++++++++++++++++++++++++++++++++-
 lib/sp-elf-symbol-resolver.h |    4 ++-
 4 files changed, 114 insertions(+), 10 deletions(-)
---
diff --git a/lib/sp-callgraph-profile.c b/lib/sp-callgraph-profile.c
index 57b0939..a3fae6f 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 e034748..916ac34 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 8d78d86..507a6d3 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 3b359af..eb25b83 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
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]