mirror of
https://github.com/varun-r-mallya/sysprof.git
synced 2025-12-31 20:36:25 +00:00
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.
This commit is contained in:
@ -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,
|
||||
|
||||
283
src/libsysprof/sysprof-mountinfo.c
Normal file
283
src/libsysprof/sysprof-mountinfo.c
Normal file
@ -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);
|
||||
}
|
||||
41
src/libsysprof/sysprof-mountinfo.h
Normal file
41
src/libsysprof/sysprof-mountinfo.h
Normal file
@ -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
|
||||
@ -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')
|
||||
|
||||
|
||||
59
src/tests/test-mountinfo.c
Normal file
59
src/tests/test-mountinfo.c
Normal file
@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user