Files
sysprof/src/libsysprof-ui/sysprof-profiler-assistant.c
Christian Hergert 6e3632c0e5 libsysprof-ui: add local symbol decoder source to profiler
This allows us to decode symbols locally at the end of a capture so that
we avoid trying to decode them on a machine without the info.
2019-05-29 15:13:01 -07:00

345 lines
12 KiB
C

/* sysprof-profiler-assistant.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-profiler-assistant"
#include "config.h"
#include <sysprof.h>
#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));
}