Files
sysprof/src/libsysprof-ui/sysprof-depth-visualizer.c
Christian Hergert 97ddf5a0cc libsysprof: port UI to new ABI
Some minor changes were necessary so that we could change the ABI in
libsysprof-capture to be free from GLib (and therefore used by GLib).

This also adds some wrappers for capture API in libsysprof so that we
can continue to use GError from UI code.
2020-07-03 22:00:34 +01:00

448 lines
12 KiB
C

/* sysprof-depth-visualizer.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-depth-visualizer"
#include "config.h"
#include <glib/gi18n.h>
#include "pointcache.h"
#include "sysprof-depth-visualizer.h"
struct _SysprofDepthVisualizer
{
SysprofVisualizer parent_instance;
SysprofCaptureReader *reader;
PointCache *points;
guint reload_source;
guint mode;
GtkAllocation last_alloc;
guint reloading : 1;
guint needs_reload : 1;
};
typedef struct
{
SysprofCaptureReader *reader;
PointCache *pc;
gint64 begin_time;
gint64 end_time;
gint64 duration;
guint max_n_addrs;
guint mode;
} State;
static void sysprof_depth_visualizer_reload (SysprofDepthVisualizer *self);
G_DEFINE_TYPE (SysprofDepthVisualizer, sysprof_depth_visualizer, SYSPROF_TYPE_VISUALIZER)
static void
state_free (State *st)
{
g_clear_pointer (&st->reader, sysprof_capture_reader_unref);
g_clear_pointer (&st->pc, point_cache_unref);
g_slice_free (State, st);
}
static bool
discover_max_n_addr (const SysprofCaptureFrame *frame,
gpointer user_data)
{
const SysprofCaptureSample *sample = (const SysprofCaptureSample *)frame;
State *st = user_data;
g_assert (frame != NULL);
g_assert (frame->type == SYSPROF_CAPTURE_FRAME_SAMPLE);
g_assert (st != NULL);
st->max_n_addrs = MAX (st->max_n_addrs, sample->n_addrs);
return TRUE;
}
static bool
build_point_cache_cb (const SysprofCaptureFrame *frame,
gpointer user_data)
{
const SysprofCaptureSample *sample = (const SysprofCaptureSample *)frame;
State *st = user_data;
gdouble x, y;
gboolean has_kernel = FALSE;
g_assert (frame != NULL);
g_assert (frame->type == SYSPROF_CAPTURE_FRAME_SAMPLE);
g_assert (st != NULL);
x = (frame->time - st->begin_time) / (gdouble)st->duration;
y = sample->n_addrs / (gdouble)st->max_n_addrs;
/* If this contains a context-switch (meaning we're going into the kernel
* to do some work, use a negative value for Y so that we know later on
* that we should draw it with a different color (after removing the negation
* on the value.
*
* We skip past the first index, which is always a context switch as it is
* our perf handler.
*/
for (guint i = 1; i < sample->n_addrs; i++)
{
SysprofAddressContext kind;
if (sysprof_address_is_context_switch (sample->addrs[i], &kind))
{
has_kernel = TRUE;
y = -y;
break;
}
}
if (!has_kernel)
point_cache_add_point_to_set (st->pc, 1, x, y);
else
point_cache_add_point_to_set (st->pc, 2, x, y);
return TRUE;
}
static void
sysprof_depth_visualizer_worker (GTask *task,
gpointer source_object,
gpointer task_data,
GCancellable *cancellable)
{
static const SysprofCaptureFrameType types[] = { SYSPROF_CAPTURE_FRAME_SAMPLE, };
g_autoptr(SysprofCaptureCursor) cursor = NULL;
SysprofCaptureCondition *condition;
State *st = task_data;
g_assert (G_IS_TASK (task));
g_assert (SYSPROF_IS_DEPTH_VISUALIZER (source_object));
g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
if (st->duration != 0)
{
cursor = sysprof_capture_cursor_new (st->reader);
condition = sysprof_capture_condition_new_where_type_in (G_N_ELEMENTS (types), types);
sysprof_capture_cursor_add_condition (cursor, g_steal_pointer (&condition));
sysprof_capture_cursor_foreach (cursor, discover_max_n_addr, st);
sysprof_capture_cursor_reset (cursor);
sysprof_capture_cursor_foreach (cursor, build_point_cache_cb, st);
}
g_task_return_pointer (task,
g_steal_pointer (&st->pc),
(GDestroyNotify) point_cache_unref);
}
static void
apply_point_cache_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
SysprofDepthVisualizer *self = (SysprofDepthVisualizer *)object;
PointCache *pc;
g_assert (SYSPROF_IS_DEPTH_VISUALIZER (self));
g_assert (G_IS_TASK (result));
self->reloading = FALSE;
if ((pc = g_task_propagate_pointer (G_TASK (result), NULL)))
{
g_clear_pointer (&self->points, point_cache_unref);
self->points = g_steal_pointer (&pc);
gtk_widget_queue_draw (GTK_WIDGET (self));
}
if (self->needs_reload)
sysprof_depth_visualizer_reload (self);
}
static void
sysprof_depth_visualizer_reload (SysprofDepthVisualizer *self)
{
g_autoptr(GTask) task = NULL;
GtkAllocation alloc;
State *st;
g_assert (SYSPROF_IS_DEPTH_VISUALIZER (self));
self->needs_reload = TRUE;
if (self->reloading)
return;
self->reloading = TRUE;
self->needs_reload = FALSE;
gtk_widget_get_allocation (GTK_WIDGET (self), &alloc);
st = g_slice_new0 (State);
st->reader = sysprof_capture_reader_ref (self->reader);
st->pc = point_cache_new ();
st->max_n_addrs = 0;
st->begin_time = sysprof_capture_reader_get_start_time (self->reader);
st->end_time = sysprof_capture_reader_get_end_time (self->reader);
st->duration = st->end_time - st->begin_time;
st->mode = self->mode;
point_cache_add_set (st->pc, 1);
point_cache_add_set (st->pc, 2);
task = g_task_new (self, NULL, apply_point_cache_cb, NULL);
g_task_set_source_tag (task, sysprof_depth_visualizer_reload);
g_task_set_task_data (task, st, (GDestroyNotify) state_free);
g_task_run_in_thread (task, sysprof_depth_visualizer_worker);
}
static void
sysprof_depth_visualizer_set_reader (SysprofVisualizer *row,
SysprofCaptureReader *reader)
{
SysprofDepthVisualizer *self = (SysprofDepthVisualizer *)row;
g_assert (SYSPROF_IS_DEPTH_VISUALIZER (self));
if (self->reader != reader)
{
if (self->reader != NULL)
{
sysprof_capture_reader_unref (self->reader);
self->reader = NULL;
}
if (reader != NULL)
{
self->reader = sysprof_capture_reader_ref (reader);
sysprof_depth_visualizer_reload (self);
}
}
}
static gboolean
sysprof_depth_visualizer_draw (GtkWidget *widget,
cairo_t *cr)
{
SysprofDepthVisualizer *self = (SysprofDepthVisualizer *)widget;
GtkAllocation alloc;
GdkRectangle clip;
const Point *points;
gboolean ret;
guint n_points = 0;
GdkRGBA user;
GdkRGBA system;
g_assert (SYSPROF_IS_DEPTH_VISUALIZER (self));
g_assert (cr != NULL);
ret = GTK_WIDGET_CLASS (sysprof_depth_visualizer_parent_class)->draw (widget, cr);
if (self->points == NULL)
return ret;
gdk_rgba_parse (&user, "#1a5fb4");
gdk_rgba_parse (&system, "#3584e4");
gtk_widget_get_allocation (widget, &alloc);
if (!gdk_cairo_get_clip_rectangle (cr, &clip))
return ret;
/* Draw user-space stacks */
if (self->mode != SYSPROF_DEPTH_VISUALIZER_KERNEL_ONLY &&
(points = point_cache_get_points (self->points, 1, &n_points)))
{
g_autofree SysprofVisualizerAbsolutePoint *out_points = NULL;
out_points = g_new (SysprofVisualizerAbsolutePoint, n_points);
sysprof_visualizer_translate_points (SYSPROF_VISUALIZER (widget),
(const SysprofVisualizerRelativePoint *)points,
n_points, out_points, n_points);
cairo_set_line_width (cr, 1.0);
gdk_cairo_set_source_rgba (cr, &user);
for (guint i = 0; i < n_points; i++)
{
gdouble x, y;
x = out_points[i].x;
y = out_points[i].y;
if (x < clip.x)
continue;
if (x > clip.x + clip.width)
break;
for (guint j = i + 1; j < n_points; j++)
{
if (out_points[j].x != x)
break;
y = MIN (y, out_points[j].y);
}
x += alloc.x;
cairo_move_to (cr, (guint)x + .5, alloc.height);
cairo_line_to (cr, (guint)x + .5, y);
}
cairo_stroke (cr);
}
/* Draw kernel-space stacks */
if (self->mode != SYSPROF_DEPTH_VISUALIZER_USER_ONLY &&
(points = point_cache_get_points (self->points, 2, &n_points)))
{
g_autofree SysprofVisualizerAbsolutePoint *out_points = NULL;
out_points = g_new (SysprofVisualizerAbsolutePoint, n_points);
sysprof_visualizer_translate_points (SYSPROF_VISUALIZER (widget),
(const SysprofVisualizerRelativePoint *)points,
n_points, out_points, n_points);
cairo_set_line_width (cr, 1.0);
gdk_cairo_set_source_rgba (cr, &system);
for (guint i = 0; i < n_points; i++)
{
gdouble x, y;
x = out_points[i].x;
y = out_points[i].y;
if (x < clip.x)
continue;
if (x > clip.x + clip.width)
break;
for (guint j = i + 1; j < n_points; j++)
{
if (out_points[j].x != x)
break;
y = MIN (y, out_points[j].y);
}
x += alloc.x;
cairo_move_to (cr, (guint)x + .5, alloc.height);
cairo_line_to (cr, (guint)x + .5, y);
}
cairo_stroke (cr);
}
return ret;
}
static gboolean
sysprof_depth_visualizer_do_reload (gpointer data)
{
SysprofDepthVisualizer *self = data;
self->reload_source = 0;
sysprof_depth_visualizer_reload (self);
return G_SOURCE_REMOVE;
}
static void
sysprof_depth_visualizer_queue_reload (SysprofDepthVisualizer *self)
{
g_assert (SYSPROF_IS_DEPTH_VISUALIZER (self));
if (self->reload_source)
g_source_remove (self->reload_source);
self->reload_source = gdk_threads_add_idle (sysprof_depth_visualizer_do_reload, self);
}
static void
sysprof_depth_visualizer_size_allocate (GtkWidget *widget,
GtkAllocation *alloc)
{
SysprofDepthVisualizer *self = (SysprofDepthVisualizer *)widget;
GTK_WIDGET_CLASS (sysprof_depth_visualizer_parent_class)->size_allocate (widget, alloc);
if (alloc->width != self->last_alloc.x ||
alloc->height != self->last_alloc.height)
{
sysprof_depth_visualizer_queue_reload (SYSPROF_DEPTH_VISUALIZER (widget));
self->last_alloc = *alloc;
}
}
static void
sysprof_depth_visualizer_finalize (GObject *object)
{
SysprofDepthVisualizer *self = (SysprofDepthVisualizer *)object;
g_clear_pointer (&self->reader, sysprof_capture_reader_unref);
if (self->reload_source)
{
g_source_remove (self->reload_source);
self->reload_source = 0;
}
G_OBJECT_CLASS (sysprof_depth_visualizer_parent_class)->finalize (object);
}
static void
sysprof_depth_visualizer_class_init (SysprofDepthVisualizerClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
SysprofVisualizerClass *row_class = SYSPROF_VISUALIZER_CLASS (klass);
object_class->finalize = sysprof_depth_visualizer_finalize;
widget_class->draw = sysprof_depth_visualizer_draw;
widget_class->size_allocate = sysprof_depth_visualizer_size_allocate;
row_class->set_reader = sysprof_depth_visualizer_set_reader;
}
static void
sysprof_depth_visualizer_init (SysprofDepthVisualizer *self)
{
}
SysprofVisualizer *
sysprof_depth_visualizer_new (SysprofDepthVisualizerMode mode)
{
SysprofDepthVisualizer *self;
g_return_val_if_fail (mode == SYSPROF_DEPTH_VISUALIZER_COMBINED ||
mode == SYSPROF_DEPTH_VISUALIZER_KERNEL_ONLY ||
mode == SYSPROF_DEPTH_VISUALIZER_USER_ONLY,
NULL);
self = g_object_new (SYSPROF_TYPE_DEPTH_VISUALIZER, NULL);
self->mode = mode;
return SYSPROF_VISUALIZER (g_steal_pointer (&self));
}