diff --git a/lib/Makefile.am b/lib/Makefile.am index b31ff98b..42da2abe 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -137,6 +137,7 @@ uiheadersdir = $(includedir)/sysprof-@API_VERSION@ uiheaders_DATA = \ sp-callgraph-view.h \ sp-cell-renderer-percent.h \ + sp-cpu-visualizer-row.h \ sp-empty-state-view.h \ sp-failed-state-view.h \ sp-line-visualizer-row.h \ @@ -154,6 +155,7 @@ libsysprof_ui_@API_VERSION@_la_SOURCES = \ $(uiheaders_DATA) \ sp-callgraph-view.c \ sp-cell-renderer-percent.c \ + sp-cpu-visualizer-row.c \ sp-empty-state-view.c \ sp-failed-state-view.c \ sp-line-visualizer-row.c \ diff --git a/lib/sp-cpu-visualizer-row.c b/lib/sp-cpu-visualizer-row.c new file mode 100644 index 00000000..cc2bc4b6 --- /dev/null +++ b/lib/sp-cpu-visualizer-row.c @@ -0,0 +1,156 @@ +/* sp-cpu-visualizer-row.c + * + * Copyright (C) 2016 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 . + */ + +#define G_LOG_DOMAIN "sp-cpu-visualizer-row" + +#include "sp-capture-condition.h" +#include "sp-capture-cursor.h" +#include "sp-cpu-visualizer-row.h" + +struct _SpCpuVisualizerRow +{ + SpLineVisualizerRow parent_instance; +}; + +G_DEFINE_TYPE (SpCpuVisualizerRow, sp_cpu_visualizer_row, SP_TYPE_LINE_VISUALIZER_ROW) + +static gboolean +sp_cpu_visualizer_counter_found (const SpCaptureFrame *frame, + gpointer user_data) +{ + const SpCaptureFrameCounterDefine *def = (SpCaptureFrameCounterDefine *)frame; + GArray *counters = user_data; + + g_assert (frame->type == SP_CAPTURE_FRAME_CTRDEF); + + for (guint i = 0; i < def->n_counters; i++) + { + if (g_str_equal (def->counters[i].category, "CPU Percent")) + { + guint id = def->counters[i].id; + g_array_append_val (counters, id); + } + } + + return TRUE; +} + +static void +sp_cpu_visualizer_row_discover_counters (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *canellable) +{ + const SpCaptureFrameType types[] = { SP_CAPTURE_FRAME_CTRDEF }; + SpCaptureReader *reader = task_data; + g_autoptr(SpCaptureCursor) cursor = NULL; + SpCpuVisualizerRow *self = source_object; + g_autoptr(GArray) counters = NULL; + + g_assert (G_IS_TASK (task)); + g_assert (SP_IS_CPU_VISUALIZER_ROW (self)); + g_assert (reader != NULL); + + counters = g_array_new (FALSE, FALSE, sizeof (guint)); + cursor = sp_capture_cursor_new (reader); + sp_capture_cursor_add_condition (cursor, sp_capture_condition_new_where_type_in (G_N_ELEMENTS (types), types)); + sp_capture_cursor_foreach (cursor, sp_cpu_visualizer_counter_found, counters); + g_task_return_pointer (task, g_steal_pointer (&counters), (GDestroyNotify)g_array_unref); +} + +static void +complete_counters (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + SpCpuVisualizerRow *self = (SpCpuVisualizerRow *)object; + g_autoptr(GArray) counters = NULL; + + g_assert (SP_IS_CPU_VISUALIZER_ROW (self)); + g_assert (G_IS_TASK (result)); + + counters = g_task_propagate_pointer (G_TASK (result), NULL); + + if (counters != NULL) + { + for (guint i = 0; i < counters->len; i++) + { + guint counter_id = g_array_index (counters, guint, i); + + sp_line_visualizer_row_add_counter (SP_LINE_VISUALIZER_ROW (self), counter_id); + } + } +} + +static void +sp_cpu_visualizer_row_set_reader (SpVisualizerRow *row, + SpCaptureReader *reader) +{ + SpCpuVisualizerRow *self = (SpCpuVisualizerRow *)row; + g_autoptr(GTask) task = NULL; + + g_assert (SP_IS_CPU_VISUALIZER_ROW (row)); + + sp_line_visualizer_row_clear (SP_LINE_VISUALIZER_ROW (row)); + + SP_VISUALIZER_ROW_CLASS (sp_cpu_visualizer_row_parent_class)->set_reader (row, reader); + + if (reader != NULL) + { + task = g_task_new (self, NULL, complete_counters, NULL); + g_task_set_source_tag (task, sp_cpu_visualizer_row_set_reader); + g_task_set_task_data (task, sp_capture_reader_copy (reader), + (GDestroyNotify)sp_capture_reader_unref); + g_task_run_in_thread (task, sp_cpu_visualizer_row_discover_counters); + } +} + +static void +sp_cpu_visualizer_row_prepare (SpLineVisualizerRow *row, + cairo_t *cr, + guint counter_id) +{ + g_assert (SP_IS_LINE_VISUALIZER_ROW (row)); + g_assert (cr != NULL); + g_assert (counter_id > 0); + + cairo_set_source_rgba (cr, 0, 0, 0, 0.4); + cairo_set_line_width (cr, 1.0); +} + +static void +sp_cpu_visualizer_row_class_init (SpCpuVisualizerRowClass *klass) +{ + SpLineVisualizerRowClass *line_class = SP_LINE_VISUALIZER_ROW_CLASS (klass); + SpVisualizerRowClass *row_class = SP_VISUALIZER_ROW_CLASS (klass); + + row_class->set_reader = sp_cpu_visualizer_row_set_reader; + + line_class->prepare = sp_cpu_visualizer_row_prepare; +} + +static void +sp_cpu_visualizer_row_init (SpCpuVisualizerRow *self) +{ +} + +GtkWidget * +sp_cpu_visualizer_row_new (void) +{ + return g_object_new (SP_TYPE_CPU_VISUALIZER_ROW, NULL); +} diff --git a/lib/sp-cpu-visualizer-row.h b/lib/sp-cpu-visualizer-row.h new file mode 100644 index 00000000..7683caae --- /dev/null +++ b/lib/sp-cpu-visualizer-row.h @@ -0,0 +1,34 @@ +/* sp-cpu-visualizer-row.h + * + * Copyright (C) 2016 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 . + */ + +#ifndef SP_CPU_VISUALIZER_ROW_H +#define SP_CPU_VISUALIZER_ROW_H + +#include "sp-line-visualizer-row.h" + +G_BEGIN_DECLS + +#define SP_TYPE_CPU_VISUALIZER_ROW (sp_cpu_visualizer_row_get_type()) + +G_DECLARE_FINAL_TYPE (SpCpuVisualizerRow, sp_cpu_visualizer_row, SP, CPU_VISUALIZER_ROW, SpLineVisualizerRow) + +GtkWidget *sp_cpu_visualizer_row_new (void); + +G_END_DECLS + +#endif /* SP_CPU_VISUALIZER_ROW_H */ diff --git a/lib/sp-line-visualizer-row.c b/lib/sp-line-visualizer-row.c index 0ae5826d..d359472a 100644 --- a/lib/sp-line-visualizer-row.c +++ b/lib/sp-line-visualizer-row.c @@ -16,6 +16,8 @@ * along with this program. If not, see . */ +#define G_LOG_DOMAIN "sp-line-visualizer-row" + #include #include "sp-line-visualizer-row.h" @@ -33,23 +35,20 @@ typedef struct GArray *values; } LineData; -struct _SpLineVisualizerRow +typedef struct { - SpVisualizerRow parent_instance; - SpCaptureReader *reader; GHashTable *counters; - GtkLabel *label; - guint needs_recalc : 1; - gint64 start_time; gint64 duration; -}; -G_DEFINE_TYPE (SpLineVisualizerRow, sp_line_visualizer_row, SP_TYPE_VISUALIZER_ROW) + guint needs_recalc : 1; +} SpLineVisualizerRowPrivate; + +G_DEFINE_TYPE_WITH_PRIVATE (SpLineVisualizerRow, sp_line_visualizer_row, SP_TYPE_VISUALIZER_ROW) enum { PROP_0, @@ -74,47 +73,56 @@ line_data_free (gpointer data) static void sp_line_visualizer_row_recalc (SpLineVisualizerRow *self) { + SpLineVisualizerRowPrivate *priv = sp_line_visualizer_row_get_instance_private (self); GHashTable *new_data; SpCaptureFrame frame; g_assert (SP_IS_LINE_VISUALIZER_ROW (self)); - self->needs_recalc = FALSE; + /* + * TODO: + * + * We want to create an index that gives us quick access to this + * data without quite so much overhead that we have now. The 16 + * bytes per data-point is pretty high. + */ - if (self->reader == NULL) + priv->needs_recalc = FALSE; + + if (priv->reader == NULL) return; - sp_capture_reader_reset (self->reader); + sp_capture_reader_reset (priv->reader); - self->start_time = sp_capture_reader_get_start_time (self->reader); - self->duration = 0; + priv->start_time = sp_capture_reader_get_start_time (priv->reader); + priv->duration = 0; new_data = g_hash_table_new_full (NULL, NULL, NULL, line_data_free); - while (sp_capture_reader_peek_frame (self->reader, &frame)) + while (sp_capture_reader_peek_frame (priv->reader, &frame)) { gint64 relative; - if (frame.time < self->start_time) - frame.time = self->start_time; + if (frame.time < priv->start_time) + frame.time = priv->start_time; - relative = frame.time - self->start_time; - if (relative > self->duration) - self->duration = relative; + relative = frame.time - priv->start_time; + if (relative > priv->duration) + priv->duration = relative; if (frame.type == SP_CAPTURE_FRAME_CTRDEF) { const SpCaptureFrameCounterDefine *def; guint i; - if (NULL == (def = sp_capture_reader_read_counter_define (self->reader))) + if (NULL == (def = sp_capture_reader_read_counter_define (priv->reader))) return; for (i = 0; i < def->n_counters; i++) { const SpCaptureCounter *ctr = &def->counters[i]; - if (g_hash_table_contains (self->counters, GSIZE_TO_POINTER (ctr->id))) + if (g_hash_table_contains (priv->counters, GSIZE_TO_POINTER (ctr->id))) { LineData *ld = g_hash_table_lookup (new_data, GSIZE_TO_POINTER (ctr->id)); LineDataItem item; @@ -128,7 +136,7 @@ sp_line_visualizer_row_recalc (SpLineVisualizerRow *self) g_hash_table_insert (new_data, GSIZE_TO_POINTER (ctr->id), ld); } - item.time = MAX (self->start_time, def->frame.time) - self->start_time; + item.time = MAX (priv->start_time, def->frame.time) - priv->start_time; item.value = ctr->value; g_array_append_val (ld->values, item); @@ -141,10 +149,10 @@ sp_line_visualizer_row_recalc (SpLineVisualizerRow *self) LineDataItem item; guint i; - if (NULL == (set = sp_capture_reader_read_counter_set (self->reader))) + if (NULL == (set = sp_capture_reader_read_counter_set (priv->reader))) return; - item.time = MAX (self->start_time, set->frame.time) - self->start_time; + item.time = MAX (priv->start_time, set->frame.time) - priv->start_time; for (i = 0; i < set->n_values; i++) { @@ -170,20 +178,12 @@ sp_line_visualizer_row_recalc (SpLineVisualizerRow *self) } } } - else if (!sp_capture_reader_skip (self->reader)) + else if (!sp_capture_reader_skip (priv->reader)) return; } - g_clear_pointer (&self->counters, g_hash_table_unref); - self->counters = new_data; -} - -static inline gdouble -calc_x (SpLineVisualizerRow *self, - gint64 time) -{ - gdouble ret = (gdouble)time / (gdouble)self->duration; - return ret; + g_clear_pointer (&priv->counters, g_hash_table_unref); + priv->counters = new_data; } static inline gdouble @@ -191,8 +191,7 @@ calc_y (SpLineVisualizerRow *self, LineData *ld, SpCaptureCounterValue value) { - gdouble ret = value.vdbl / 100.0; - return ret; + return value.vdbl / 100.0; } static gboolean @@ -200,64 +199,55 @@ sp_line_visualizer_row_draw (GtkWidget *widget, cairo_t *cr) { SpLineVisualizerRow *self = (SpLineVisualizerRow *)widget; + SpLineVisualizerRowPrivate *priv = sp_line_visualizer_row_get_instance_private (self); GtkAllocation alloc; GHashTableIter iter; + gdouble duration; gboolean ret; gpointer key, val; - guint i; g_assert (SP_IS_LINE_VISUALIZER_ROW (widget)); g_assert (cr != NULL); - if (self->needs_recalc) + if (priv->needs_recalc) sp_line_visualizer_row_recalc (self); gtk_widget_get_allocation (widget, &alloc); ret = GTK_WIDGET_CLASS (sp_line_visualizer_row_parent_class)->draw (widget, cr); - cairo_save (cr); + duration = (gdouble)priv->duration; - g_hash_table_iter_init (&iter, self->counters); + g_hash_table_iter_init (&iter, priv->counters); while (g_hash_table_iter_next (&iter, &key, &val)) { LineData *ld = val; - gdouble last_x = 0; - gdouble last_y = 0; + cairo_save (cr); cairo_move_to (cr, 0, alloc.height); - for (i = 0; i < ld->values->len; i++) + for (guint i = 0; i < ld->values->len; i++) { LineDataItem *item = &g_array_index (ld->values, LineDataItem, i); gdouble x; gdouble y; - x = calc_x (self, item->time) * alloc.width; - y = alloc.height - (calc_y (self, ld, item->value) * alloc.height); - - //g_print ("x=%d y =%d\n", (int)x, (int)y); + x = (item->time / duration) * alloc.width; + y = alloc.height - ((item->value.vdbl / 100.0) * alloc.height); cairo_line_to (cr, x, y); - - (void)last_x; - (void)last_y; - - last_x = x; - last_y = x; } cairo_line_to (cr, alloc.width, alloc.height); cairo_line_to (cr, 0, alloc.height); - cairo_set_line_width (cr, 1.0); - cairo_set_source_rgba (cr, 0, 0, 0, 0.4); - cairo_stroke_preserve (cr); - cairo_set_source_rgba (cr, 0, 0, 0, 0.2); - cairo_fill (cr); - } - cairo_restore (cr); + SP_LINE_VISUALIZER_ROW_GET_CLASS (self)->prepare (self, cr, ld->id); + + cairo_stroke_preserve (cr); + cairo_fill (cr); + cairo_restore (cr); + } return ret; } @@ -267,37 +257,52 @@ sp_line_visualizer_row_set_reader (SpVisualizerRow *row, SpCaptureReader *reader) { SpLineVisualizerRow *self = (SpLineVisualizerRow *)row; + SpLineVisualizerRowPrivate *priv = sp_line_visualizer_row_get_instance_private (self); g_assert (SP_IS_LINE_VISUALIZER_ROW (self)); - if (self->reader != reader) + if (priv->reader != reader) { - g_clear_pointer (&self->reader, sp_capture_reader_unref); + if (priv->reader != NULL) + { + sp_capture_reader_unref (priv->reader); + priv->reader = NULL; + priv->start_time = 0; + priv->duration = 0; + } - if (reader) - { - self->reader = sp_capture_reader_ref (reader); - self->start_time = 0; - self->duration = 0; - } - - /* TODO */ - sp_line_visualizer_row_add_counter (self, 1); - sp_line_visualizer_row_add_counter (self, 3); - sp_line_visualizer_row_add_counter (self, 5); - sp_line_visualizer_row_add_counter (self, 7); + if (reader != NULL) + { + priv->reader = sp_capture_reader_ref (reader); + priv->start_time = 0; + priv->duration = 0; + priv->needs_recalc = TRUE; + } gtk_widget_queue_draw (GTK_WIDGET (self)); } } +static void +sp_line_visualizer_row_real_prepare (SpLineVisualizerRow *self, + cairo_t *cr, + guint counter_id) +{ + g_assert (SP_IS_LINE_VISUALIZER_ROW (self)); + g_assert (cr != NULL); + + cairo_set_line_width (cr, 1.0); + cairo_set_source_rgba (cr, 0, 0, 0, 0.4); +} + static void sp_line_visualizer_row_finalize (GObject *object) { SpLineVisualizerRow *self = (SpLineVisualizerRow *)object; + SpLineVisualizerRowPrivate *priv = sp_line_visualizer_row_get_instance_private (self); - g_clear_pointer (&self->counters, g_hash_table_unref); - g_clear_pointer (&self->reader, sp_capture_reader_unref); + g_clear_pointer (&priv->counters, g_hash_table_unref); + g_clear_pointer (&priv->reader, sp_capture_reader_unref); G_OBJECT_CLASS (sp_line_visualizer_row_parent_class)->finalize (object); } @@ -309,11 +314,12 @@ sp_line_visualizer_row_get_property (GObject *object, GParamSpec *pspec) { SpLineVisualizerRow *self = SP_LINE_VISUALIZER_ROW (object); + SpLineVisualizerRowPrivate *priv = sp_line_visualizer_row_get_instance_private (self); switch (prop_id) { case PROP_TITLE: - g_object_get_property (G_OBJECT (self->label), "label", value); + g_object_get_property (G_OBJECT (priv->label), "label", value); break; default: @@ -328,11 +334,12 @@ sp_line_visualizer_row_set_property (GObject *object, GParamSpec *pspec) { SpLineVisualizerRow *self = SP_LINE_VISUALIZER_ROW (object); + SpLineVisualizerRowPrivate *priv = sp_line_visualizer_row_get_instance_private (self); switch (prop_id) { case PROP_TITLE: - g_object_set_property (G_OBJECT (self->label), "label", value); + g_object_set_property (G_OBJECT (priv->label), "label", value); break; default: @@ -347,6 +354,8 @@ sp_line_visualizer_row_class_init (SpLineVisualizerRowClass *klass) GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); SpVisualizerRowClass *visualizer_class = SP_VISUALIZER_ROW_CLASS (klass); + klass->prepare = sp_line_visualizer_row_real_prepare; + object_class->finalize = sp_line_visualizer_row_finalize; object_class->get_property = sp_line_visualizer_row_get_property; object_class->set_property = sp_line_visualizer_row_set_property; @@ -368,19 +377,21 @@ sp_line_visualizer_row_class_init (SpLineVisualizerRowClass *klass) static void sp_line_visualizer_row_init (SpLineVisualizerRow *self) { - PangoAttrList *attrs = NULL; + SpLineVisualizerRowPrivate *priv = sp_line_visualizer_row_get_instance_private (self); + PangoAttrList *attrs = pango_attr_list_new (); - self->counters = g_hash_table_new (NULL, NULL); + priv->counters = g_hash_table_new (NULL, NULL); - attrs = pango_attr_list_new (); pango_attr_list_insert (attrs, pango_attr_scale_new (PANGO_SCALE_SMALL * PANGO_SCALE_SMALL)); - self->label = g_object_new (GTK_TYPE_LABEL, + + priv->label = g_object_new (GTK_TYPE_LABEL, "attributes", attrs, "visible", TRUE, "xalign", 0.0f, "yalign", 0.0f, NULL); - gtk_container_add (GTK_CONTAINER (self), GTK_WIDGET (self->label)); + gtk_container_add (GTK_CONTAINER (self), GTK_WIDGET (priv->label)); + pango_attr_list_unref (attrs); } @@ -388,9 +399,28 @@ void sp_line_visualizer_row_add_counter (SpLineVisualizerRow *self, guint counter_id) { - g_assert (SP_IS_LINE_VISUALIZER_ROW (self)); + SpLineVisualizerRowPrivate *priv = sp_line_visualizer_row_get_instance_private (self); + + g_assert (SP_IS_LINE_VISUALIZER_ROW (self)); + g_assert (priv->counters != NULL); + + g_hash_table_insert (priv->counters, GSIZE_TO_POINTER (counter_id), NULL); + + if (SP_LINE_VISUALIZER_ROW_GET_CLASS (self)->counter_added) + SP_LINE_VISUALIZER_ROW_GET_CLASS (self)->counter_added (self, counter_id); + + priv->needs_recalc = TRUE; - self->needs_recalc = TRUE; - g_hash_table_insert (self->counters, GSIZE_TO_POINTER (counter_id), NULL); gtk_widget_queue_draw (GTK_WIDGET (self)); } + +void +sp_line_visualizer_row_clear (SpLineVisualizerRow *self) +{ + SpLineVisualizerRowPrivate *priv = sp_line_visualizer_row_get_instance_private (self); + + g_return_if_fail (SP_IS_LINE_VISUALIZER_ROW (self)); + + g_hash_table_remove_all (priv->counters); + gtk_widget_queue_resize (GTK_WIDGET (self)); +} diff --git a/lib/sp-line-visualizer-row.h b/lib/sp-line-visualizer-row.h index bf6aef83..61c5cf2c 100644 --- a/lib/sp-line-visualizer-row.h +++ b/lib/sp-line-visualizer-row.h @@ -25,9 +25,38 @@ G_BEGIN_DECLS #define SP_TYPE_LINE_VISUALIZER_ROW (sp_line_visualizer_row_get_type()) -G_DECLARE_FINAL_TYPE (SpLineVisualizerRow, sp_line_visualizer_row, SP, LINE_VISUALIZER_ROW, SpVisualizerRow) +G_DECLARE_DERIVABLE_TYPE (SpLineVisualizerRow, sp_line_visualizer_row, SP, LINE_VISUALIZER_ROW, SpVisualizerRow) + +struct _SpLineVisualizerRowClass +{ + SpVisualizerRowClass parent_class; + + void (*counter_added) (SpLineVisualizerRow *self, + guint counter_id); + void (*prepare) (SpLineVisualizerRow *self, + cairo_t *cr, + guint counter_id); + + gpointer _reserved1; + gpointer _reserved2; + gpointer _reserved3; + gpointer _reserved4; + gpointer _reserved5; + gpointer _reserved6; + gpointer _reserved7; + gpointer _reserved8; + gpointer _reserved9; + gpointer _reserved10; + gpointer _reserved11; + gpointer _reserved12; + gpointer _reserved13; + gpointer _reserved14; + gpointer _reserved15; + gpointer _reserved16; +}; GtkWidget *sp_line_visualizer_row_new (void); +void sp_line_visualizer_row_clear (SpLineVisualizerRow *self); void sp_line_visualizer_row_add_counter (SpLineVisualizerRow *self, guint counter_id); diff --git a/src/resources/ui/sp-window.ui b/src/resources/ui/sp-window.ui index 1949bde7..cd7cd000 100644 --- a/src/resources/ui/sp-window.ui +++ b/src/resources/ui/sp-window.ui @@ -172,7 +172,7 @@ true - + CPU 75 false