mirror of
https://github.com/varun-r-mallya/sysprof.git
synced 2026-02-11 23:51:06 +00:00
libsysprof: attempt to new spawned process information
If a new process is spawned after the recording has started (processes spawned *by* sysprof are done before recording starts) then try to extract information about that process and append it to the recording. The goal here is to get enough process information to actually decode the process without creating fork()/exec() amplification. Related GNOME/gnome-builder#2090
This commit is contained in:
@ -32,6 +32,8 @@
|
|||||||
struct _SysprofLinuxInstrument
|
struct _SysprofLinuxInstrument
|
||||||
{
|
{
|
||||||
SysprofInstrument parent_instance;
|
SysprofInstrument parent_instance;
|
||||||
|
SysprofRecording *recording;
|
||||||
|
GHashTable *seen;
|
||||||
};
|
};
|
||||||
|
|
||||||
G_DEFINE_FINAL_TYPE (SysprofLinuxInstrument, sysprof_linux_instrument, SYSPROF_TYPE_INSTRUMENT)
|
G_DEFINE_FINAL_TYPE (SysprofLinuxInstrument, sysprof_linux_instrument, SYSPROF_TYPE_INSTRUMENT)
|
||||||
@ -147,9 +149,10 @@ populate_overlays (SysprofRecording *recording,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static DexFuture *
|
static DexFuture *
|
||||||
add_process_info (SysprofRecording *recording,
|
add_process_info (SysprofLinuxInstrument *self,
|
||||||
GVariant *process_info,
|
SysprofRecording *recording,
|
||||||
gint64 at_time)
|
GVariant *process_info,
|
||||||
|
gint64 at_time)
|
||||||
{
|
{
|
||||||
g_autoptr(SysprofPodman) podman = NULL;
|
g_autoptr(SysprofPodman) podman = NULL;
|
||||||
g_autoptr(GPtrArray) futures = NULL;
|
g_autoptr(GPtrArray) futures = NULL;
|
||||||
@ -157,6 +160,8 @@ add_process_info (SysprofRecording *recording,
|
|||||||
GVariantIter iter;
|
GVariantIter iter;
|
||||||
GVariant *pidinfo;
|
GVariant *pidinfo;
|
||||||
|
|
||||||
|
g_assert (SYSPROF_IS_LINUX_INSTRUMENT (self));
|
||||||
|
g_assert (SYSPROF_IS_RECORDING (recording));
|
||||||
g_assert (process_info != NULL);
|
g_assert (process_info != NULL);
|
||||||
g_assert (g_variant_is_of_type (process_info, G_VARIANT_TYPE ("aa{sv}")));
|
g_assert (g_variant_is_of_type (process_info, G_VARIANT_TYPE ("aa{sv}")));
|
||||||
|
|
||||||
@ -183,6 +188,8 @@ add_process_info (SysprofRecording *recording,
|
|||||||
if (!g_variant_dict_lookup (&dict, "pid", "i", &pid))
|
if (!g_variant_dict_lookup (&dict, "pid", "i", &pid))
|
||||||
goto skip;
|
goto skip;
|
||||||
|
|
||||||
|
g_hash_table_add (self->seen, GINT_TO_POINTER (pid));
|
||||||
|
|
||||||
if (!g_variant_dict_lookup (&dict, "cmdline", "&s", &cmdline))
|
if (!g_variant_dict_lookup (&dict, "cmdline", "&s", &cmdline))
|
||||||
cmdline = "";
|
cmdline = "";
|
||||||
|
|
||||||
@ -233,20 +240,21 @@ add_process_info (SysprofRecording *recording,
|
|||||||
static DexFuture *
|
static DexFuture *
|
||||||
sysprof_linux_instrument_prepare_fiber (gpointer user_data)
|
sysprof_linux_instrument_prepare_fiber (gpointer user_data)
|
||||||
{
|
{
|
||||||
SysprofRecording *recording = user_data;
|
SysprofLinuxInstrument *self = user_data;
|
||||||
g_autoptr(GDBusConnection) bus = NULL;
|
g_autoptr(GDBusConnection) bus = NULL;
|
||||||
g_autoptr(GVariant) process_info_reply = NULL;
|
g_autoptr(GVariant) process_info_reply = NULL;
|
||||||
g_autoptr(GVariant) process_info = NULL;
|
g_autoptr(GVariant) process_info = NULL;
|
||||||
g_autoptr(GError) error = NULL;
|
g_autoptr(GError) error = NULL;
|
||||||
gint64 at_time;
|
gint64 at_time;
|
||||||
|
|
||||||
g_assert (SYSPROF_IS_RECORDING (recording));
|
g_assert (SYSPROF_IS_LINUX_INSTRUMENT (self));
|
||||||
|
g_assert (SYSPROF_IS_RECORDING (self->recording));
|
||||||
|
|
||||||
/* First get some basic information about the system into the capture. We can
|
/* First get some basic information about the system into the capture. We can
|
||||||
* get the contents for all of these concurrently.
|
* get the contents for all of these concurrently.
|
||||||
*/
|
*/
|
||||||
if (!dex_await (dex_future_all (_sysprof_recording_add_file (recording, "/proc/cpuinfo", TRUE),
|
if (!dex_await (dex_future_all (_sysprof_recording_add_file (self->recording, "/proc/cpuinfo", TRUE),
|
||||||
_sysprof_recording_add_file (recording, "/proc/mounts", TRUE),
|
_sysprof_recording_add_file (self->recording, "/proc/mounts", TRUE),
|
||||||
NULL),
|
NULL),
|
||||||
&error))
|
&error))
|
||||||
return dex_future_new_for_error (g_steal_pointer (&error));
|
return dex_future_new_for_error (g_steal_pointer (&error));
|
||||||
@ -273,7 +281,7 @@ sysprof_linux_instrument_prepare_fiber (gpointer user_data)
|
|||||||
|
|
||||||
/* Add process records for each of the processes discovered */
|
/* Add process records for each of the processes discovered */
|
||||||
process_info = g_variant_get_child_value (process_info_reply, 0);
|
process_info = g_variant_get_child_value (process_info_reply, 0);
|
||||||
dex_await (add_process_info (recording, process_info, at_time), NULL);
|
dex_await (add_process_info (self, self->recording, process_info, at_time), NULL);
|
||||||
|
|
||||||
return dex_future_new_for_boolean (TRUE);
|
return dex_future_new_for_boolean (TRUE);
|
||||||
}
|
}
|
||||||
@ -282,39 +290,194 @@ static DexFuture *
|
|||||||
sysprof_linux_instrument_prepare (SysprofInstrument *instrument,
|
sysprof_linux_instrument_prepare (SysprofInstrument *instrument,
|
||||||
SysprofRecording *recording)
|
SysprofRecording *recording)
|
||||||
{
|
{
|
||||||
g_assert (SYSPROF_IS_INSTRUMENT (instrument));
|
SysprofLinuxInstrument *self = (SysprofLinuxInstrument *)instrument;
|
||||||
|
|
||||||
|
g_assert (SYSPROF_IS_INSTRUMENT (self));
|
||||||
g_assert (SYSPROF_IS_RECORDING (recording));
|
g_assert (SYSPROF_IS_RECORDING (recording));
|
||||||
|
|
||||||
|
g_set_object (&self->recording, recording);
|
||||||
|
|
||||||
return dex_scheduler_spawn (NULL, 0,
|
return dex_scheduler_spawn (NULL, 0,
|
||||||
sysprof_linux_instrument_prepare_fiber,
|
sysprof_linux_instrument_prepare_fiber,
|
||||||
g_object_ref (recording),
|
g_object_ref (self),
|
||||||
g_object_unref);
|
g_object_unref);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
SysprofRecording *recording;
|
||||||
|
GPtrArray *paths;
|
||||||
|
int pid;
|
||||||
|
} ProcessStarted;
|
||||||
|
|
||||||
|
static void
|
||||||
|
process_started_free (gpointer data)
|
||||||
|
{
|
||||||
|
ProcessStarted *state = data;
|
||||||
|
|
||||||
|
g_clear_object (&state->recording);
|
||||||
|
g_clear_pointer (&state->paths, g_ptr_array_unref);
|
||||||
|
g_free (state);
|
||||||
|
}
|
||||||
|
|
||||||
|
static DexFuture *
|
||||||
|
process_started_cb (DexFuture *completed,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
ProcessStarted *state = user_data;
|
||||||
|
guint size;
|
||||||
|
|
||||||
|
g_assert (DEX_IS_FUTURE_SET (completed));
|
||||||
|
g_assert (state != NULL);
|
||||||
|
g_assert (SYSPROF_IS_RECORDING (state->recording));
|
||||||
|
g_assert (state->pid > 0);
|
||||||
|
g_assert (state->paths != NULL);
|
||||||
|
|
||||||
|
size = dex_future_set_get_size (DEX_FUTURE_SET (completed));
|
||||||
|
|
||||||
|
g_assert (state->paths->len == size);
|
||||||
|
|
||||||
|
for (guint i = 0; i < size; i++)
|
||||||
|
{
|
||||||
|
DexFuture *future = dex_future_set_get_future_at (DEX_FUTURE_SET (completed), i);
|
||||||
|
g_autoptr(GError) error = NULL;
|
||||||
|
g_autoptr(GVariant) variant = dex_await_variant (dex_ref (future), &error);
|
||||||
|
const char *path = g_ptr_array_index (state->paths, i);
|
||||||
|
const char *bytestring = NULL;
|
||||||
|
|
||||||
|
if (error != NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (variant == NULL || !g_variant_is_of_type (variant, G_VARIANT_TYPE ("(ay)")))
|
||||||
|
g_return_val_if_reached (NULL);
|
||||||
|
|
||||||
|
g_variant_get (variant, "(^&ay)", &bytestring);
|
||||||
|
|
||||||
|
if (bytestring == NULL)
|
||||||
|
g_return_val_if_reached (NULL);
|
||||||
|
|
||||||
|
_sysprof_recording_add_file_data (state->recording, path, bytestring, -1, TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return dex_future_new_for_boolean (TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
static DexFuture *
|
static DexFuture *
|
||||||
sysprof_linux_instrument_process_started (SysprofInstrument *instrument,
|
sysprof_linux_instrument_process_started (SysprofInstrument *instrument,
|
||||||
SysprofRecording *recording,
|
SysprofRecording *recording,
|
||||||
int pid,
|
int pid,
|
||||||
const char *comm)
|
const char *comm)
|
||||||
{
|
{
|
||||||
g_assert (SYSPROF_IS_INSTRUMENT (instrument));
|
SysprofLinuxInstrument *self = (SysprofLinuxInstrument *)instrument;
|
||||||
|
g_autoptr(GDBusConnection) bus = NULL;
|
||||||
|
g_autoptr(GError) error = NULL;
|
||||||
|
g_autofree char *mountinfo_path = NULL;
|
||||||
|
g_autofree char *flatpak_info_path = NULL;
|
||||||
|
DexFuture *mountinfo;
|
||||||
|
DexFuture *flatpak_info;
|
||||||
|
ProcessStarted *state;
|
||||||
|
|
||||||
|
g_assert (SYSPROF_IS_LINUX_INSTRUMENT (self));
|
||||||
g_assert (SYSPROF_IS_RECORDING (recording));
|
g_assert (SYSPROF_IS_RECORDING (recording));
|
||||||
|
|
||||||
/* We can't actually make a request of sysprofd to get
|
/* Most information for new processes we'll get inline, like the address
|
||||||
* information on the process right now because that would
|
* layout for memory mappings. But we still want to get information about
|
||||||
* create a new process to verify our authentication.
|
* flatpak and mountinfo.
|
||||||
*
|
|
||||||
* And that would just amplify this request further.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
return dex_future_new_for_boolean (TRUE);
|
/* Avoid spawn amplification by monitoring processes which are spawned in
|
||||||
|
* reaction to the requests we make of sysprofd. This in particular sucks
|
||||||
|
* because it means we are not very good at profiling these processes (at
|
||||||
|
* least the ones spawned while recording). However, if we did try to get
|
||||||
|
* information on them, we'd end up just chaising our tail.
|
||||||
|
*/
|
||||||
|
if (comm != NULL)
|
||||||
|
{
|
||||||
|
/* Only check up to 15 chars because that is what we can get from Perf
|
||||||
|
* via PERF_RECORD_COMM messages.
|
||||||
|
*/
|
||||||
|
if (g_str_has_prefix (comm, "systemd-userwor") ||
|
||||||
|
g_str_has_prefix (comm, "pkla-check-auth"))
|
||||||
|
return dex_future_new_for_boolean (TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Shortcut if we've already seen this */
|
||||||
|
if (g_hash_table_contains (self->seen, GINT_TO_POINTER (pid)))
|
||||||
|
return dex_future_new_for_boolean (TRUE);
|
||||||
|
g_hash_table_add (self->seen, GINT_TO_POINTER (pid));
|
||||||
|
|
||||||
|
/* Get the bus synchronously so we don't have to suspend the fiber. This
|
||||||
|
* will always return immediately anyway.
|
||||||
|
*/
|
||||||
|
if (!(bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error)))
|
||||||
|
return dex_future_new_for_error (g_steal_pointer (&error));
|
||||||
|
|
||||||
|
mountinfo_path = g_strdup_printf ("/proc/%d/mountinfo", pid);
|
||||||
|
mountinfo = dex_dbus_connection_call (bus,
|
||||||
|
"org.gnome.Sysprof3",
|
||||||
|
"/org/gnome/Sysprof3",
|
||||||
|
"org.gnome.Sysprof3.Service",
|
||||||
|
"GetProcFile",
|
||||||
|
g_variant_new ("(^ay)", mountinfo_path),
|
||||||
|
G_VARIANT_TYPE ("(ay)"),
|
||||||
|
G_DBUS_CALL_FLAGS_NONE,
|
||||||
|
-1);
|
||||||
|
|
||||||
|
flatpak_info_path = g_strdup_printf ("/proc/%d/root/.flatpak-info", pid);
|
||||||
|
flatpak_info = dex_dbus_connection_call (bus,
|
||||||
|
"org.gnome.Sysprof3",
|
||||||
|
"/org/gnome/Sysprof3",
|
||||||
|
"org.gnome.Sysprof3.Service",
|
||||||
|
"GetProcFile",
|
||||||
|
g_variant_new ("(^ay)", flatpak_info_path),
|
||||||
|
G_VARIANT_TYPE ("(ay)"),
|
||||||
|
G_DBUS_CALL_FLAGS_NONE,
|
||||||
|
-1);
|
||||||
|
|
||||||
|
state = g_new0 (ProcessStarted, 1);
|
||||||
|
state->pid = pid;
|
||||||
|
state->recording = g_object_ref (recording);
|
||||||
|
state->paths = g_ptr_array_new_with_free_func (g_free);
|
||||||
|
|
||||||
|
g_ptr_array_add (state->paths, g_steal_pointer (&mountinfo_path));
|
||||||
|
g_ptr_array_add (state->paths, g_steal_pointer (&flatpak_info_path));
|
||||||
|
|
||||||
|
return dex_future_then (dex_future_any (mountinfo, flatpak_info, NULL),
|
||||||
|
process_started_cb,
|
||||||
|
state,
|
||||||
|
process_started_free);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sysprof_linux_instrument_dispose (GObject *object)
|
||||||
|
{
|
||||||
|
SysprofLinuxInstrument *self = (SysprofLinuxInstrument *)object;
|
||||||
|
|
||||||
|
g_clear_object (&self->recording);
|
||||||
|
g_hash_table_remove_all (self->seen);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (sysprof_linux_instrument_parent_class)->dispose (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sysprof_linux_instrument_finalize (GObject *object)
|
||||||
|
{
|
||||||
|
SysprofLinuxInstrument *self = (SysprofLinuxInstrument *)object;
|
||||||
|
|
||||||
|
g_clear_pointer (&self->seen, g_hash_table_unref);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (sysprof_linux_instrument_parent_class)->finalize (object);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
sysprof_linux_instrument_class_init (SysprofLinuxInstrumentClass *klass)
|
sysprof_linux_instrument_class_init (SysprofLinuxInstrumentClass *klass)
|
||||||
{
|
{
|
||||||
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||||
SysprofInstrumentClass *instrument_class = SYSPROF_INSTRUMENT_CLASS (klass);
|
SysprofInstrumentClass *instrument_class = SYSPROF_INSTRUMENT_CLASS (klass);
|
||||||
|
|
||||||
|
object_class->dispose = sysprof_linux_instrument_dispose;
|
||||||
|
object_class->finalize = sysprof_linux_instrument_finalize;
|
||||||
|
|
||||||
instrument_class->list_required_policy = sysprof_linux_instrument_list_required_policy;
|
instrument_class->list_required_policy = sysprof_linux_instrument_list_required_policy;
|
||||||
instrument_class->prepare = sysprof_linux_instrument_prepare;
|
instrument_class->prepare = sysprof_linux_instrument_prepare;
|
||||||
instrument_class->process_started = sysprof_linux_instrument_process_started;
|
instrument_class->process_started = sysprof_linux_instrument_process_started;
|
||||||
@ -323,6 +486,7 @@ sysprof_linux_instrument_class_init (SysprofLinuxInstrumentClass *klass)
|
|||||||
static void
|
static void
|
||||||
sysprof_linux_instrument_init (SysprofLinuxInstrument *self)
|
sysprof_linux_instrument_init (SysprofLinuxInstrument *self)
|
||||||
{
|
{
|
||||||
|
self->seen = g_hash_table_new (NULL, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
SysprofInstrument *
|
SysprofInstrument *
|
||||||
|
|||||||
Reference in New Issue
Block a user