[sysprof] libsysprof: add mountinfo helper



commit 7e7657dd0353cd4d8e57dd498e7373e3b50024e4
Author: Christian Hergert <chergert redhat com>
Date:   Fri Aug 2 12:56:14 2019 -0700

    libsysprof: add mountinfo helper
    
    The goal of this helper is to simplify the process of parsing information
    about mounts and the mountinfo for per-process maps. We should be able
    to change sysprof-proc-source to use this and have better support for
    getting the libraries within different mount namespaces.

 src/libsysprof/meson.build         |   1 +
 src/libsysprof/sysprof-mountinfo.c | 283 +++++++++++++++++++++++++++++++++++++
 src/libsysprof/sysprof-mountinfo.h |  41 ++++++
 src/tests/meson.build              |   7 +
 src/tests/test-mountinfo.c         |  59 ++++++++
 5 files changed, 391 insertions(+)
---
diff --git a/src/libsysprof/meson.build b/src/libsysprof/meson.build
index 14f9905..2f21dd3 100644
--- a/src/libsysprof/meson.build
+++ b/src/libsysprof/meson.build
@@ -69,6 +69,7 @@ libsysprof_private_sources = [
   'sysprof-kallsyms.c',
   'sysprof-line-reader.c',
   'sysprof-map-lookaside.c',
+  'sysprof-mountinfo.c',
   'sysprof-polkit.c',
   'sysprof-symbol-map.c',
   ipc_service_src,
diff --git a/src/libsysprof/sysprof-mountinfo.c b/src/libsysprof/sysprof-mountinfo.c
new file mode 100644
index 0000000..82d9e0f
--- /dev/null
+++ b/src/libsysprof/sysprof-mountinfo.c
@@ -0,0 +1,283 @@
+/* sysprof-mountinfo.c
+ *
+ * Copyright 2019 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "sysprof-mountinfo"
+
+#include "config.h"
+
+#include "sysprof-mountinfo.h"
+
+typedef struct
+{
+  gchar *device;
+  gchar *mountpoint;
+} Mount;
+
+typedef struct
+{
+  gchar *host_path;
+  gchar *mount_path;
+} Mountinfo;
+
+struct _SysprofMountinfo
+{
+  GArray     *mounts;
+  GArray     *mountinfos;
+  GHashTable *dircache;
+};
+
+enum {
+  COLUMN_MOUNT_ID = 0,
+  COLUMN_MOUNT_PARENT_ID,
+  COLUMN_MAJOR_MINOR,
+  COLUMN_ROOT,
+  COLUMN_MOUNT_POINT,
+  COLUMN_MOUNT_OPTIONS,
+};
+
+static void
+mount_clear (gpointer data)
+{
+  Mount *m = data;
+
+  g_free (m->device);
+  g_free (m->mountpoint);
+}
+
+static void
+mountinfo_clear (gpointer data)
+{
+  Mountinfo *m = data;
+
+  g_free (m->host_path);
+  g_free (m->mount_path);
+}
+
+SysprofMountinfo *
+sysprof_mountinfo_new (void)
+{
+  SysprofMountinfo *self;
+
+  self = g_slice_new0 (SysprofMountinfo);
+  self->mounts = g_array_new (FALSE, FALSE, sizeof (Mount));
+  g_array_set_clear_func (self->mounts, mount_clear);
+  self->mountinfos = g_array_new (FALSE, FALSE, sizeof (Mountinfo));
+  g_array_set_clear_func (self->mountinfos, mountinfo_clear);
+  self->dircache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+
+  return g_steal_pointer (&self);
+}
+
+void
+sysprof_mountinfo_free (SysprofMountinfo *self)
+{
+  g_clear_pointer (&self->mounts, g_array_unref);
+  g_clear_pointer (&self->mountinfos, g_array_unref);
+  g_clear_pointer (&self->dircache, g_hash_table_unref);
+  g_slice_free (SysprofMountinfo, self);
+}
+
+gchar *
+sysprof_mountinfo_translate (SysprofMountinfo *self,
+                             const gchar      *path)
+{
+  g_autofree gchar *dir = NULL;
+  const gchar *translate;
+
+  g_assert (self != NULL);
+
+  if (path == NULL)
+    return NULL;
+
+  /* First try the dircache by looking up the translated parent
+   * directory and appending basename to it.
+   */
+  dir = g_path_get_dirname (path);
+
+  if ((translate = g_hash_table_lookup (self->dircache, dir)))
+    {
+      g_autofree gchar *name = g_path_get_basename (path);
+      return g_build_filename (translate, name, NULL);
+    }
+
+  for (guint i = 0; i < self->mountinfos->len; i++)
+    {
+      const Mountinfo *m = &g_array_index (self->mountinfos, Mountinfo, i);
+
+      if (g_str_has_prefix (path, m->mount_path))
+        {
+          gchar *ret;
+
+          ret = g_build_filename (m->host_path, path + strlen (m->mount_path), NULL);
+          g_hash_table_insert (self->dircache,
+                               g_steal_pointer (&dir),
+                               g_path_get_dirname (ret));
+          return ret;
+        }
+    }
+
+  return NULL;
+}
+
+void
+sysprof_mountinfo_parse_mounts (SysprofMountinfo *self,
+                                const gchar      *contents)
+{
+  g_auto(GStrv) lines = NULL;
+
+  g_assert (self != NULL);
+  g_assert (self->mounts != NULL);
+  g_assert (contents != NULL);
+
+  lines = g_strsplit (contents, "\n", 0);
+
+  for (guint i = 0; lines[i]; i++)
+    {
+      g_auto(GStrv) parts = g_strsplit (lines[i], " ", 3);
+      Mount m;
+
+      /* Field 1 and 2 are "device" and "mountpoint" */
+      if (parts[0] == NULL || parts[1] == NULL)
+        continue;
+
+      /* Replace encoded space "\040" with ' ' */
+      if (strstr (parts[1], "\\040") != NULL)
+        {
+          g_auto(GStrv) ep = g_strsplit (parts[1], "\\040", 0);
+          g_free (parts[1]);
+          parts[1] = g_strjoinv (" ", ep);
+        }
+
+      m.device = g_strdup (parts[0]);
+      m.mountpoint = g_strdup (parts[1]);
+      g_array_append_val (self->mounts, m);
+    }
+}
+
+void
+sysprof_mountinfo_reset (SysprofMountinfo *self)
+{
+  g_assert (self != NULL);
+  g_assert (self->mountinfos != NULL);
+
+  /* Keep mounts, but release mountinfos */
+  if (self->mountinfos->len)
+    g_array_remove_range (self->mountinfos, 0, self->mountinfos->len);
+  g_hash_table_remove_all (self->dircache);
+}
+
+static const gchar *
+get_device_mount (SysprofMountinfo *self,
+                  const gchar      *device)
+{
+  g_assert (self != NULL);
+  g_assert (self->mounts != NULL);
+  g_assert (device != NULL);
+
+  for (guint i = 0; i < self->mounts->len; i++)
+    {
+      const Mount *m = &g_array_index (self->mounts, Mount, i);
+
+      if (strcmp (device, m->device) == 0)
+        return m->mountpoint;
+    }
+
+  return NULL;
+}
+
+static void
+sysprof_mountinfo_parse_mountinfo_line (SysprofMountinfo *self,
+                                        const gchar      *line)
+{
+  g_auto(GStrv) parts = NULL;
+  const gchar *prefix;
+  const gchar *src;
+  Mountinfo m;
+  gsize n_parts;
+  guint i;
+
+  g_assert (self != NULL);
+  g_assert (self->mounts != NULL);
+  g_assert (self->mountinfos != NULL);
+
+  parts = g_strsplit (line, " ", 0);
+  n_parts = g_strv_length (parts);
+
+  if (n_parts < 10)
+    return;
+
+  /* The device identifier is the 2nd column after "-" */
+  for (i = 5; i < n_parts; i++)
+    {
+      if (strcmp (parts[i], "-") == 0)
+        break;
+    }
+  if (i >= n_parts || parts[i][0] != '-' || parts[i+1] == NULL || parts[i+2] == NULL)
+    return;
+
+  prefix = get_device_mount (self, parts[i+2]);
+
+  src = parts[COLUMN_ROOT];
+  while (*src == '/')
+    src++;
+
+  if (prefix != NULL)
+    m.host_path = g_build_filename (prefix, src, NULL);
+  else
+    m.host_path = g_strdup (src);
+
+  m.mount_path = g_strdup (parts[COLUMN_MOUNT_POINT]);
+  g_array_append_val (self->mountinfos, m);
+}
+
+static gint
+sort_by_length (gconstpointer a,
+                gconstpointer b)
+{
+  const Mountinfo *mpa = a;
+  const Mountinfo *mpb = b;
+  gsize alen = strlen (mpa->mount_path);
+  gsize blen = strlen (mpb->mount_path);
+
+  if (alen > blen)
+    return -1;
+  else if (blen > alen)
+    return 1;
+  else
+    return 0;
+}
+
+void
+sysprof_mountinfo_parse_mountinfo (SysprofMountinfo *self,
+                                   const gchar      *contents)
+{
+  g_auto(GStrv) lines = NULL;
+
+  g_assert (self != NULL);
+  g_assert (self->mounts != NULL);
+  g_assert (self->mountinfos != NULL);
+
+  lines = g_strsplit (contents, "\n", 0);
+
+  for (guint i = 0; lines[i]; i++)
+    sysprof_mountinfo_parse_mountinfo_line (self, lines[i]);
+
+  g_array_sort (self->mountinfos, sort_by_length);
+}
diff --git a/src/libsysprof/sysprof-mountinfo.h b/src/libsysprof/sysprof-mountinfo.h
new file mode 100644
index 0000000..85c661e
--- /dev/null
+++ b/src/libsysprof/sysprof-mountinfo.h
@@ -0,0 +1,41 @@
+/* sysprof-mountinfo.h
+ *
+ * Copyright 2019 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+typedef struct _SysprofMountinfo SysprofMountinfo;
+
+SysprofMountinfo *sysprof_mountinfo_new             (void);
+void              sysprof_mountinfo_parse_mounts    (SysprofMountinfo *self,
+                                                     const gchar      *contents);
+void              sysprof_mountinfo_parse_mountinfo (SysprofMountinfo *self,
+                                                     const gchar      *contents);
+void              sysprof_mountinfo_reset           (SysprofMountinfo *self);
+gchar            *sysprof_mountinfo_translate       (SysprofMountinfo *self,
+                                                     const gchar      *path);
+void              sysprof_mountinfo_free            (SysprofMountinfo *self);
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofMountinfo, sysprof_mountinfo_free)
+
+G_END_DECLS
diff --git a/src/tests/meson.build b/src/tests/meson.build
index 53a3785..690ec5c 100644
--- a/src/tests/meson.build
+++ b/src/tests/meson.build
@@ -43,6 +43,13 @@ test_addr_decode = executable('test-addr-decode', 'test-addr-decode.c',
   dependencies: test_deps,
 )
 
+test_mountinfo = executable('test-mountinfo',
+  [ 'test-mountinfo.c',
+    '../libsysprof/sysprof-mountinfo.c'],
+        c_args: test_cflags,
+  dependencies: test_deps,
+)
+
 
 if get_option('enable_gtk')
 
diff --git a/src/tests/test-mountinfo.c b/src/tests/test-mountinfo.c
new file mode 100644
index 0000000..e390211
--- /dev/null
+++ b/src/tests/test-mountinfo.c
@@ -0,0 +1,59 @@
+/* test-mountinfo.c
+ *
+ * Copyright 2019 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#include "sysprof-mountinfo.h"
+
+gint
+main (gint   argc,
+      gchar *argv[])
+{
+  g_autoptr(SysprofMountinfo) info = NULL;
+  g_autofree gchar *contents = NULL;
+  g_auto(GStrv) lines = NULL;
+  g_autoptr(GError) error = NULL;
+  g_autofree gchar *lookup = NULL;
+  gsize len;
+
+  if (argc != 3)
+    {
+      g_printerr ("usage: %s MOUNTINFO_FILE PATH_TO_TRANSLATE\n", argv[0]);
+      return 1;
+    }
+
+  info = sysprof_mountinfo_new ();
+
+  if (!g_file_get_contents ("/proc/mounts", &contents, &len, &error))
+    g_error ("%s", error->message);
+  sysprof_mountinfo_parse_mounts (info, contents);
+  g_free (g_steal_pointer (&contents));
+
+  if (!g_file_get_contents (argv[1], &contents, &len, &error))
+    g_error ("%s", error->message);
+  sysprof_mountinfo_parse_mountinfo (info, contents);
+
+  lookup = sysprof_mountinfo_translate (info, argv[2]);
+
+  if (lookup)
+    g_print ("%s\n", lookup);
+  else
+    g_print ("%s\n", argv[2]);
+
+  return 0;
+}


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