mirror of
https://github.com/varun-r-mallya/sysprof.git
synced 2025-12-31 20:36:25 +00:00
proc: translate paths found in alternate mount namespaces
If we come across a map that is in another namespace, we might need to translate it to the filesystem/path inside that mount namespace. Otherwise, we won't be able to accurately decode ELF symbols due to loading the incorrect binary/debug files.
This commit is contained in:
@ -151,11 +151,154 @@ sp_proc_source_populate_process (SpProcSource *self,
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
strv_at_least_len (GStrv strv,
|
||||
guint len)
|
||||
{
|
||||
for (guint i = 0; i < len; i++)
|
||||
{
|
||||
if (strv[i] == NULL)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gchar *
|
||||
find_mount (GStrv mounts,
|
||||
const gchar *mount)
|
||||
{
|
||||
gsize len = strlen (mount);
|
||||
|
||||
for (guint i = 0; mounts[i]; i++)
|
||||
{
|
||||
const gchar *endptr;
|
||||
const gchar *begin;
|
||||
|
||||
if (!g_str_has_prefix (mounts[i], mount))
|
||||
continue;
|
||||
|
||||
if (mounts[i][len] != ' ')
|
||||
continue;
|
||||
|
||||
begin = &mounts[i][len + 1];
|
||||
endptr = strchr (begin, ' ');
|
||||
if (endptr == NULL)
|
||||
continue;
|
||||
|
||||
return g_strndup (begin, endptr - begin);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* sp_proc_source_translate_path:
|
||||
* @file: the path to the file inside target mount namespace
|
||||
* @mountinfo: mount info to locate translated path
|
||||
* @out_file: (out): location for the translated path
|
||||
* @out_file_len: length of @out_file
|
||||
*
|
||||
* This function will use @mountinfo to locate the longest common prefix
|
||||
* to determine which mount contains @file. That will be used to translate
|
||||
* the path pointed to by @file into the host namespace.
|
||||
*
|
||||
* The result is stored in @out_file and will always be NULL terminated.
|
||||
*/
|
||||
static void
|
||||
sp_proc_source_translate_path (const gchar *file,
|
||||
GStrv mountinfo,
|
||||
GStrv mounts,
|
||||
gchar *out_file,
|
||||
gsize out_file_len)
|
||||
{
|
||||
g_autofree gchar *closest_host = NULL;
|
||||
g_autofree gchar *closest_guest = NULL;
|
||||
g_autofree gchar *closest_mount = NULL;
|
||||
gsize closest_len = 0;
|
||||
|
||||
g_assert (file != NULL);
|
||||
g_assert (g_str_has_prefix (file, "/newroot/"));
|
||||
g_assert (mountinfo != NULL);
|
||||
g_assert (out_file != NULL);
|
||||
|
||||
if (!g_str_has_prefix (file, "/newroot/"))
|
||||
goto failure;
|
||||
|
||||
file += strlen ("/newroot");
|
||||
|
||||
for (guint i = 0; mountinfo[i] != NULL; i++)
|
||||
{
|
||||
g_auto(GStrv) parts = g_strsplit (mountinfo[i], " ", 11);
|
||||
const gchar *host;
|
||||
const gchar *guest;
|
||||
const gchar *mount;
|
||||
|
||||
/*
|
||||
* Not ideal to do the string split here, but it is much easier
|
||||
* to just do that until we get this right, and then improve
|
||||
* things later when a strok()/etc parser.
|
||||
*/
|
||||
|
||||
if (!strv_at_least_len (parts, 10))
|
||||
continue;
|
||||
|
||||
host = parts[3];
|
||||
guest = parts[4];
|
||||
mount = parts[9];
|
||||
|
||||
if (g_str_has_prefix (file, guest))
|
||||
{
|
||||
gsize len = strlen (guest);
|
||||
|
||||
if (len > closest_len && (file[len] == '\0' || file[len] == '/'))
|
||||
{
|
||||
g_free (closest_host);
|
||||
g_free (closest_guest);
|
||||
g_free (closest_mount);
|
||||
|
||||
closest_guest = g_strdup (guest);
|
||||
closest_host = g_strdup (host);
|
||||
closest_mount = g_strdup (mount);
|
||||
|
||||
closest_len = len;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (closest_len > 0)
|
||||
{
|
||||
/*
|
||||
* The translated path is relative to the mount. So we need to add that
|
||||
* prefix to this as well, based on matching it from the closest_mount.
|
||||
*/
|
||||
g_autofree gchar *mount = NULL;
|
||||
|
||||
mount = find_mount (mounts, closest_mount);
|
||||
|
||||
if (mount != NULL)
|
||||
{
|
||||
g_autofree gchar *path = NULL;
|
||||
|
||||
path = g_build_filename (mount, closest_host, file + strlen (closest_guest), NULL);
|
||||
g_strlcpy (out_file, path, out_file_len);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
failure:
|
||||
/* Fallback to just copying the source */
|
||||
g_strlcpy (out_file, file, out_file_len);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_proc_source_populate_maps (SpProcSource *self,
|
||||
GPid pid)
|
||||
GPid pid,
|
||||
GStrv mounts)
|
||||
{
|
||||
g_auto(GStrv) lines = NULL;
|
||||
g_auto(GStrv) mountinfo = NULL;
|
||||
guint i;
|
||||
|
||||
g_assert (SP_IS_PROC_SOURCE (self));
|
||||
@ -164,9 +307,14 @@ sp_proc_source_populate_maps (SpProcSource *self,
|
||||
if (NULL == (lines = proc_readlines ("/proc/%d/maps", pid)))
|
||||
return;
|
||||
|
||||
if (NULL == (mountinfo = proc_readlines ("/proc/%d/mountinfo", pid)))
|
||||
return;
|
||||
|
||||
for (i = 0; lines [i] != NULL; i++)
|
||||
{
|
||||
gchar file[256];
|
||||
gchar translated[256];
|
||||
const gchar *fileptr = file;
|
||||
gulong start;
|
||||
gulong end;
|
||||
gulong offset;
|
||||
@ -197,6 +345,24 @@ sp_proc_source_populate_maps (SpProcSource *self,
|
||||
inode = 0;
|
||||
}
|
||||
|
||||
if (g_str_has_prefix (file, "/newroot/"))
|
||||
{
|
||||
/*
|
||||
* If this file starts with /newroot/, then it is in a different
|
||||
* mount-namespace from our profiler process. This means that we need
|
||||
* to translate the filename to the real path on disk inside our
|
||||
* (hopefully the default) mount-namespace. To do this, we have to
|
||||
* look at /proc/$pid/mountinfo to locate the longest-common-prefix
|
||||
* for the path.
|
||||
*/
|
||||
sp_proc_source_translate_path (file,
|
||||
mountinfo,
|
||||
mounts,
|
||||
translated,
|
||||
sizeof translated);
|
||||
fileptr = translated;
|
||||
}
|
||||
|
||||
sp_capture_writer_add_map (self->writer,
|
||||
SP_CAPTURE_CURRENT_TIME,
|
||||
-1,
|
||||
@ -205,18 +371,22 @@ sp_proc_source_populate_maps (SpProcSource *self,
|
||||
end,
|
||||
offset,
|
||||
inode,
|
||||
file);
|
||||
fileptr);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sp_proc_source_populate (SpProcSource *self)
|
||||
{
|
||||
g_auto(GStrv) mounts = NULL;
|
||||
const gchar *name;
|
||||
GDir *dir;
|
||||
|
||||
g_assert (SP_IS_PROC_SOURCE (self));
|
||||
|
||||
if (NULL == (mounts = proc_readlines ("/proc/mounts")))
|
||||
return;
|
||||
|
||||
if (self->pids->len > 0)
|
||||
{
|
||||
guint i;
|
||||
@ -226,7 +396,7 @@ sp_proc_source_populate (SpProcSource *self)
|
||||
GPid pid = g_array_index (self->pids, GPid, i);
|
||||
|
||||
sp_proc_source_populate_process (self, pid);
|
||||
sp_proc_source_populate_maps (self, pid);
|
||||
sp_proc_source_populate_maps (self, pid, mounts);
|
||||
}
|
||||
|
||||
return;
|
||||
@ -245,7 +415,7 @@ sp_proc_source_populate (SpProcSource *self)
|
||||
continue;
|
||||
|
||||
sp_proc_source_populate_process (self, pid);
|
||||
sp_proc_source_populate_maps (self, pid);
|
||||
sp_proc_source_populate_maps (self, pid, mounts);
|
||||
}
|
||||
|
||||
g_dir_close (dir);
|
||||
|
||||
Reference in New Issue
Block a user