mirror of
https://github.com/varun-r-mallya/sysprof.git
synced 2025-12-31 20:36:25 +00:00
proc: discover filesystem root for podman containers
For processes we find in a podman container, we can sniff the libpod cgroup scope. Using that we can translate into the podman layer that contains the files. With that, future work could find the proper .so when resolving based on alternate roots for the process.
This commit is contained in:
@ -113,6 +113,7 @@ endif
|
||||
libsysprof_pkg_deps = [
|
||||
dependency('gio-2.0', version: glib_req_version),
|
||||
dependency('gio-unix-2.0', version: glib_req_version),
|
||||
dependency('json-glib-1.0'),
|
||||
polkit_dep,
|
||||
libsysprof_capture_deps,
|
||||
]
|
||||
|
||||
@ -39,6 +39,7 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <json-glib/json-glib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@ -53,9 +54,16 @@ struct _SysprofProcSource
|
||||
SysprofCaptureWriter *writer;
|
||||
GArray *pids;
|
||||
SysprofMountinfo *mountinfo;
|
||||
GHashTable *podman;
|
||||
guint is_ready : 1;
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char *id;
|
||||
char **layers;
|
||||
} PodmanInfo;
|
||||
|
||||
static void source_iface_init (SysprofSourceInterface *iface);
|
||||
|
||||
G_DEFINE_TYPE_EXTENDED (SysprofProcSource, sysprof_proc_source, G_TYPE_OBJECT, 0,
|
||||
@ -161,6 +169,147 @@ pid_is_interesting (SysprofProcSource *self,
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
podman_info_free (gpointer data)
|
||||
{
|
||||
PodmanInfo *info = data;
|
||||
|
||||
g_free (info->id);
|
||||
g_strfreev (info->layers);
|
||||
g_free (info);
|
||||
}
|
||||
|
||||
static void
|
||||
update_containers_json (SysprofProcSource *self)
|
||||
{
|
||||
g_autoptr(JsonParser) parser = NULL;
|
||||
g_autofree gchar *path = NULL;
|
||||
JsonNode *root;
|
||||
JsonArray *ar;
|
||||
guint n_items;
|
||||
|
||||
g_assert (SYSPROF_IS_PROC_SOURCE (self));
|
||||
|
||||
parser = json_parser_new ();
|
||||
path = g_build_filename (g_get_user_data_dir (),
|
||||
"containers",
|
||||
"storage",
|
||||
"overlay-containers",
|
||||
"containers.json",
|
||||
NULL);
|
||||
|
||||
if (!json_parser_load_from_file (parser, path, NULL) ||
|
||||
!(root = json_parser_get_root (parser)) ||
|
||||
!JSON_NODE_HOLDS_ARRAY (root) ||
|
||||
!(ar = json_node_get_array (root)))
|
||||
return;
|
||||
|
||||
n_items = json_array_get_length (ar);
|
||||
|
||||
for (guint i = 0; i < n_items; i++)
|
||||
{
|
||||
JsonObject *item = json_array_get_object_element (ar, i);
|
||||
PodmanInfo *info;
|
||||
const char *id;
|
||||
const char *layer;
|
||||
|
||||
if (item == NULL)
|
||||
continue;
|
||||
|
||||
id = json_object_get_string_member (item, "id");
|
||||
layer = json_object_get_string_member (item, "layer");
|
||||
|
||||
if (id == NULL || layer == NULL)
|
||||
continue;
|
||||
|
||||
info = g_new0 (PodmanInfo, 1);
|
||||
info->id = g_strdup (id);
|
||||
info->layers = g_new0 (char *, 2);
|
||||
info->layers[0] = g_strdup (layer);
|
||||
info->layers[1] = NULL;
|
||||
|
||||
g_hash_table_insert (self->podman, info->id, info);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sysprof_proc_source_populate_pid_podman (SysprofProcSource *self,
|
||||
GPid pid,
|
||||
const char *container)
|
||||
{
|
||||
PodmanInfo *info;
|
||||
|
||||
g_assert (SYSPROF_IS_PROC_SOURCE (self));
|
||||
g_assert (container != NULL);
|
||||
|
||||
if (!(info = g_hash_table_lookup (self->podman, container)))
|
||||
{
|
||||
update_containers_json (self);
|
||||
info = g_hash_table_lookup (self->podman, container);
|
||||
}
|
||||
|
||||
if (info != NULL)
|
||||
{
|
||||
for (guint i = 0; info->layers[i]; i++)
|
||||
{
|
||||
g_autofree char *path = g_build_filename (g_get_user_data_dir (),
|
||||
"containers",
|
||||
"storage",
|
||||
"overlay",
|
||||
info->layers[i],
|
||||
"diff",
|
||||
NULL);
|
||||
sysprof_capture_writer_add_pid_root (self->writer,
|
||||
SYSPROF_CAPTURE_CURRENT_TIME,
|
||||
-1,
|
||||
pid,
|
||||
path,
|
||||
i);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
sysprof_proc_source_populate_pid_root (SysprofProcSource *self,
|
||||
GPid pid,
|
||||
const char *cgroup)
|
||||
{
|
||||
static GRegex *regex;
|
||||
GMatchInfo *match_info = NULL;
|
||||
|
||||
g_assert (SYSPROF_IS_PROC_SOURCE (self));
|
||||
g_assert (cgroup != NULL);
|
||||
|
||||
/* This function tries to discover the podman container that contains the
|
||||
* process identified on the host as @pid. We can only do anything with this
|
||||
* if the pids are in containers that the running user controls (so that we
|
||||
* can actually access the overlays).
|
||||
*
|
||||
* This stuff, and I want to emphasize, is a giant hack. Just like containers
|
||||
* are on Linux. But if we are really careful, we can make this work for the
|
||||
* one particular use case I care about, which is podman/toolbox on Fedora
|
||||
* Workstation/Silverblue.
|
||||
*
|
||||
* -- Christian
|
||||
*/
|
||||
|
||||
if (regex == NULL)
|
||||
{
|
||||
regex = g_regex_new ("libpod-([a-z0-9]{64})\\.scope", G_REGEX_OPTIMIZE, 0, NULL);
|
||||
g_assert (regex != NULL);
|
||||
}
|
||||
|
||||
if (g_regex_match (regex, cgroup, 0, &match_info))
|
||||
{
|
||||
char *word = g_match_info_fetch (match_info, 1);
|
||||
sysprof_proc_source_populate_pid_podman (self, pid, word);
|
||||
g_free (word);
|
||||
}
|
||||
|
||||
g_match_info_free (match_info);
|
||||
}
|
||||
|
||||
static void
|
||||
sysprof_proc_source_populate (SysprofProcSource *self,
|
||||
GVariant *info)
|
||||
@ -191,6 +340,7 @@ sysprof_proc_source_populate (SysprofProcSource *self,
|
||||
const gchar *comm;
|
||||
const gchar *mountinfo;
|
||||
const gchar *maps;
|
||||
const gchar *cgroup;
|
||||
gint32 pid;
|
||||
|
||||
g_variant_dict_init (&dict, pidinfo);
|
||||
@ -211,8 +361,12 @@ sysprof_proc_source_populate (SysprofProcSource *self,
|
||||
if (!g_variant_dict_lookup (&dict, "maps", "&s", &maps))
|
||||
maps = "";
|
||||
|
||||
if (!g_variant_dict_lookup (&dict, "cgroup", "&s", &cgroup))
|
||||
cgroup = "";
|
||||
|
||||
sysprof_proc_source_populate_process (self, pid, *cmdline ? cmdline : comm);
|
||||
sysprof_proc_source_populate_maps (self, pid, maps, mountinfo);
|
||||
sysprof_proc_source_populate_pid_root (self, pid, cgroup);
|
||||
|
||||
skip:
|
||||
g_variant_dict_clear (&dict);
|
||||
@ -253,7 +407,7 @@ sysprof_proc_source_start (SysprofSource *source)
|
||||
g_assert (self->writer != NULL);
|
||||
|
||||
sysprof_helpers_get_process_info_async (helpers,
|
||||
"pid,maps,mountinfo,cmdline,comm",
|
||||
"pid,maps,mountinfo,cmdline,comm,cgroup",
|
||||
NULL,
|
||||
sysprof_proc_source_get_process_info_cb,
|
||||
g_object_ref (self));
|
||||
@ -362,6 +516,7 @@ sysprof_proc_source_finalize (GObject *object)
|
||||
g_clear_pointer (&self->writer, sysprof_capture_writer_unref);
|
||||
g_clear_pointer (&self->pids, g_array_unref);
|
||||
g_clear_pointer (&self->mountinfo, sysprof_mountinfo_free);
|
||||
g_clear_pointer (&self->podman, g_hash_table_unref);
|
||||
|
||||
G_OBJECT_CLASS (sysprof_proc_source_parent_class)->finalize (object);
|
||||
}
|
||||
@ -379,6 +534,7 @@ sysprof_proc_source_init (SysprofProcSource *self)
|
||||
{
|
||||
self->pids = g_array_new (FALSE, FALSE, sizeof (GPid));
|
||||
self->mountinfo = sysprof_mountinfo_new ();
|
||||
self->podman = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, podman_info_free);
|
||||
}
|
||||
|
||||
SysprofSource *
|
||||
|
||||
Reference in New Issue
Block a user