/* sysprof-profiler-assistant.c * * Copyright 2019 Christian Hergert * * 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 . * * SPDX-License-Identifier: GPL-3.0-or-later */ #define G_LOG_DOMAIN "sysprof-profiler-assistant" #include "config.h" #include #include "sysprof-platform.h" #include "sysprof-aid-icon.h" #include "sysprof-cpu-aid.h" #include "sysprof-environ-editor.h" #include "sysprof-profiler-assistant.h" #include "sysprof-proxy-aid.h" #include "sysprof-process-model-row.h" #include "sysprof-ui-private.h" struct _SysprofProfilerAssistant { GtkBin parent_instance; /* Template Objects */ GtkButton *record_button; GtkEntry *command_line; GtkRevealer *process_revealer; GtkListBox *process_list_box; SysprofEnvironEditor *environ_editor; GtkFlowBox *aid_flow_box; GtkSwitch *whole_system_switch; GtkSwitch *launch_switch; GtkSwitch *inherit_switch; }; enum { START_RECORDING, N_SIGNALS }; static guint signals [N_SIGNALS]; G_DEFINE_TYPE (SysprofProfilerAssistant, sysprof_profiler_assistant, GTK_TYPE_BIN) /** * sysprof_profiler_assistant_new: * * Create a new #SysprofProfilerAssistant. * * Returns: (transfer full): a newly created #SysprofProfilerAssistant * * Since: 3.34 */ GtkWidget * sysprof_profiler_assistant_new (void) { return g_object_new (SYSPROF_TYPE_PROFILER_ASSISTANT, NULL); } static void sysprof_profiler_assistant_aid_activated_cb (SysprofProfilerAssistant *self, SysprofAidIcon *icon, GtkFlowBox *flow_box) { g_assert (SYSPROF_IS_PROFILER_ASSISTANT (self)); g_assert (SYSPROF_IS_AID_ICON (icon)); g_assert (GTK_IS_FLOW_BOX (flow_box)); sysprof_aid_icon_toggle (icon); } static GtkWidget * create_process_row_cb (gpointer item_, gpointer user_data) { SysprofProcessModelItem *item = item_; g_assert (SYSPROF_IS_PROCESS_MODEL_ITEM (item)); return sysprof_process_model_row_new (item); } static void sysprof_profiler_assistant_notify_reveal_child_cb (SysprofProfilerAssistant *self, GParamSpec *pspec, GtkRevealer *revealer) { g_assert (SYSPROF_IS_PROFILER_ASSISTANT (self)); g_assert (GTK_IS_REVEALER (revealer)); if (gtk_revealer_get_reveal_child (revealer)) { g_autoptr(SysprofProcessModel) model = NULL; model = sysprof_process_model_new (); gtk_list_box_bind_model (self->process_list_box, G_LIST_MODEL (model), create_process_row_cb, NULL, NULL); sysprof_process_model_reload (model); } } static void sysprof_profiler_assistant_row_activated_cb (SysprofProfilerAssistant *self, SysprofProcessModelRow *row, GtkListBox *list_box) { g_assert (SYSPROF_PROFILER_ASSISTANT (self)); g_assert (SYSPROF_IS_PROCESS_MODEL_ROW (row)); g_assert (GTK_IS_LIST_BOX (list_box)); sysprof_process_model_row_set_selected (row, !sysprof_process_model_row_get_selected (row)); } static void sysprof_profiler_assistant_command_line_changed_cb (SysprofProfilerAssistant *self, GtkEntry *entry) { g_auto(GStrv) argv = NULL; GtkStyleContext *style_context; const gchar *text; gint argc; g_assert (SYSPROF_IS_PROFILER_ASSISTANT (self)); g_assert (GTK_IS_ENTRY (entry)); style_context = gtk_widget_get_style_context (GTK_WIDGET (entry)); text = gtk_entry_get_text (entry); if (text == NULL || text[0] == 0 || g_shell_parse_argv (text, &argc, &argv, NULL)) gtk_style_context_remove_class (style_context, GTK_STYLE_CLASS_ERROR); else gtk_style_context_add_class (style_context, GTK_STYLE_CLASS_ERROR); } static void sysprof_profiler_assistant_foreach_cb (GtkWidget *widget, SysprofProfiler *profiler) { g_assert (GTK_IS_WIDGET (widget)); g_assert (SYSPROF_IS_PROFILER (profiler)); if (SYSPROF_IS_PROCESS_MODEL_ROW (widget) && sysprof_process_model_row_get_selected (SYSPROF_PROCESS_MODEL_ROW (widget))) { SysprofProcessModelItem *item; GPid pid; item = sysprof_process_model_row_get_item (SYSPROF_PROCESS_MODEL_ROW (widget)); pid = sysprof_process_model_item_get_pid (item); sysprof_profiler_add_pid (profiler, pid); } else if (SYSPROF_IS_AID_ICON (widget)) { if (sysprof_aid_icon_is_selected (SYSPROF_AID_ICON (widget))) { SysprofAid *aid = sysprof_aid_icon_get_aid (SYSPROF_AID_ICON (widget)); sysprof_aid_prepare (aid, profiler); } } } static void sysprof_profiler_assistant_record_clicked_cb (SysprofProfilerAssistant *self, GtkButton *button) { g_autoptr(SysprofProfiler) profiler = NULL; g_autoptr(SysprofCaptureWriter) writer = NULL; g_autoptr(SysprofSource) symbols_source = NULL; #ifdef __linux__ g_autoptr(SysprofSource) proc_source = NULL; #endif gint fd; g_assert (SYSPROF_IS_PROFILER_ASSISTANT (self)); g_assert (GTK_IS_BUTTON (button)); gtk_widget_set_sensitive (GTK_WIDGET (self), FALSE); /* Setup a writer immediately */ if (-1 == (fd = sysprof_memfd_create ("[sysprof-capture]")) || !(writer = sysprof_capture_writer_new_from_fd (fd, 0))) { if (fd != -1) close (fd); return; } profiler = sysprof_local_profiler_new (); sysprof_profiler_set_writer (profiler, writer); /* Add pids to profiler */ gtk_container_foreach (GTK_CONTAINER (self->process_list_box), (GtkCallback) sysprof_profiler_assistant_foreach_cb, profiler); /* Setup whole system profiling */ sysprof_profiler_set_whole_system (profiler, gtk_switch_get_active (self->whole_system_switch)); if (gtk_switch_get_active (self->launch_switch)) { g_auto(GStrv) argv = NULL; g_auto(GStrv) env = NULL; SysprofEnviron *environ; const gchar *command; gint argc; command = gtk_entry_get_text (self->command_line); g_shell_parse_argv (command, &argc, &argv, NULL); sysprof_profiler_set_spawn (profiler, TRUE); sysprof_profiler_set_spawn_argv (profiler, (const gchar * const *)argv); environ = sysprof_environ_editor_get_environ (self->environ_editor); env = sysprof_environ_get_environ (environ); sysprof_profiler_set_spawn_env (profiler, (const gchar * const *)env); sysprof_profiler_set_spawn_inherit_environ (profiler, gtk_switch_get_active (self->inherit_switch)); } #ifdef __linux__ /* Always add the proc source */ proc_source = sysprof_proc_source_new (); sysprof_profiler_add_source (profiler, proc_source); #endif /* Always add symbol decoder to save to file immediately */ symbols_source = sysprof_symbols_source_new (); sysprof_profiler_add_source (profiler, symbols_source); /* Now allow the aids to add their sources */ gtk_container_foreach (GTK_CONTAINER (self->aid_flow_box), (GtkCallback) sysprof_profiler_assistant_foreach_cb, profiler); g_signal_emit (self, signals [START_RECORDING], 0, profiler); } static void sysprof_profiler_assistant_class_init (SysprofProfilerAssistantClass *klass) { GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); /** * SysprofProfilerAssistant::start-recording: * @self: a #SysprofProfilerAssistant * @profiler: a #SysprofProfiler * * This signal is emitted when a new profiling session should start. */ signals [START_RECORDING] = g_signal_new ("start-recording", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, SYSPROF_TYPE_PROFILER); gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/sysprof/ui/sysprof-profiler-assistant.ui"); gtk_widget_class_bind_template_child (widget_class, SysprofProfilerAssistant, aid_flow_box); gtk_widget_class_bind_template_child (widget_class, SysprofProfilerAssistant, command_line); gtk_widget_class_bind_template_child (widget_class, SysprofProfilerAssistant, environ_editor); gtk_widget_class_bind_template_child (widget_class, SysprofProfilerAssistant, process_list_box); gtk_widget_class_bind_template_child (widget_class, SysprofProfilerAssistant, process_revealer); gtk_widget_class_bind_template_child (widget_class, SysprofProfilerAssistant, record_button); gtk_widget_class_bind_template_child (widget_class, SysprofProfilerAssistant, whole_system_switch); gtk_widget_class_bind_template_child (widget_class, SysprofProfilerAssistant, launch_switch); gtk_widget_class_bind_template_child (widget_class, SysprofProfilerAssistant, inherit_switch); g_type_ensure (SYSPROF_TYPE_AID_ICON); g_type_ensure (SYSPROF_TYPE_CPU_AID); g_type_ensure (SYSPROF_TYPE_PROXY_AID); g_type_ensure (SYSPROF_TYPE_ENVIRON_EDITOR); } static void sysprof_profiler_assistant_init (SysprofProfilerAssistant *self) { g_autoptr(SysprofEnviron) environ = sysprof_environ_new (); gtk_widget_init_template (GTK_WIDGET (self)); g_signal_connect_object (self->record_button, "clicked", G_CALLBACK (sysprof_profiler_assistant_record_clicked_cb), self, G_CONNECT_SWAPPED); g_signal_connect_object (self->command_line, "changed", G_CALLBACK (sysprof_profiler_assistant_command_line_changed_cb), self, G_CONNECT_SWAPPED); g_signal_connect_object (self->process_list_box, "row-activated", G_CALLBACK (sysprof_profiler_assistant_row_activated_cb), self, G_CONNECT_SWAPPED); g_signal_connect_object (self->process_revealer, "notify::reveal-child", G_CALLBACK (sysprof_profiler_assistant_notify_reveal_child_cb), self, G_CONNECT_SWAPPED); g_signal_connect_object (self->aid_flow_box, "child-activated", G_CALLBACK (sysprof_profiler_assistant_aid_activated_cb), self, G_CONNECT_SWAPPED); sysprof_environ_editor_set_environ (self->environ_editor, environ); } void _sysprof_profiler_assistant_focus_record (SysprofProfilerAssistant *self) { g_return_if_fail (SYSPROF_IS_PROFILER_ASSISTANT (self)); gtk_widget_grab_focus (GTK_WIDGET (self->record_button)); }