From 99ff8f2a5c632a1063af91126cbd3fefb8363f20 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Fri, 15 Apr 2016 06:23:53 -0700 Subject: [PATCH] wip: visualizers --- lib/Makefile.am | 6 + lib/sp-hostinfo-source.c | 343 ++++++++++++++++++++++++++++ lib/sp-hostinfo-source.h | 35 +++ lib/sp-line-visualizer-row.c | 396 +++++++++++++++++++++++++++++++++ lib/sp-line-visualizer-row.h | 36 +++ lib/sp-visualizer-row.c | 41 ++++ lib/sp-visualizer-row.h | 52 +++++ lib/sysprof-ui.h | 2 + lib/sysprof.h | 1 + src/resources/theme/shared.css | 4 + src/resources/ui/sp-window.ui | 28 ++- src/sp-window.c | 14 +- tools/sysprof-cli.c | 3 + 13 files changed, 958 insertions(+), 3 deletions(-) create mode 100644 lib/sp-hostinfo-source.c create mode 100644 lib/sp-hostinfo-source.h create mode 100644 lib/sp-line-visualizer-row.c create mode 100644 lib/sp-line-visualizer-row.h create mode 100644 lib/sp-visualizer-row.c create mode 100644 lib/sp-visualizer-row.h diff --git a/lib/Makefile.am b/lib/Makefile.am index 710d4f31..d6f6a901 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -52,6 +52,7 @@ headers_DATA = \ sp-elf-symbol-resolver.h \ sp-error.h \ sp-gjs-source.h \ + sp-hostinfo-source.h \ sp-jitmap-symbol-resolver.h \ sp-kernel-symbol.h \ sp-kernel-symbol-resolver.h \ @@ -76,6 +77,7 @@ libsysprof_@API_VERSION@_la_SOURCES = \ sp-elf-symbol-resolver.c \ sp-error.c \ sp-gjs-source.c \ + sp-hostinfo-source.c \ sp-jitmap-symbol-resolver.c \ sp-kernel-symbol.c \ sp-kernel-symbol-resolver.c \ @@ -133,12 +135,14 @@ uiheaders_DATA = \ sp-cell-renderer-percent.h \ sp-empty-state-view.h \ sp-failed-state-view.h \ + sp-line-visualizer-row.h \ sp-model-filter.h \ sp-process-model.h \ sp-process-model-item.h \ sp-process-model-row.h \ sp-profiler-menu-button.h \ sp-recording-state-view.h \ + sp-visualizer-row.h \ sysprof-ui.h \ $(NULL) @@ -148,12 +152,14 @@ libsysprof_ui_@API_VERSION@_la_SOURCES = \ sp-cell-renderer-percent.c \ sp-empty-state-view.c \ sp-failed-state-view.c \ + sp-line-visualizer-row.c \ sp-model-filter.c \ sp-process-model.c \ sp-process-model-item.c \ sp-process-model-row.c \ sp-profiler-menu-button.c \ sp-recording-state-view.c \ + sp-visualizer-row.c \ $(NULL) libsysprof_ui_@API_VERSION@_la_CFLAGS = \ diff --git a/lib/sp-hostinfo-source.c b/lib/sp-hostinfo-source.c new file mode 100644 index 00000000..ea40d9d9 --- /dev/null +++ b/lib/sp-hostinfo-source.c @@ -0,0 +1,343 @@ +/* sp-hostinfo-source.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 . + */ + +#include +#include +#include + +#include "sp-hostinfo-source.h" + +struct _SpHostinfoSource +{ + GObject parent_instance; + + guint handler; + gint n_cpu; + + SpCaptureWriter *writer; + GArray *cpu_info; +}; + +typedef struct +{ + gint counter_base; + gdouble total; + gdouble freq; + glong last_user; + glong last_idle; + glong last_system; + glong last_nice; + glong last_iowait; + glong last_irq; + glong last_softirq; + glong last_steal; + glong last_guest; + glong last_guest_nice; +} CpuInfo; + +static void source_iface_init (SpSourceInterface *iface); + +G_DEFINE_TYPE_EXTENDED (SpHostinfoSource, sp_hostinfo_source, G_TYPE_OBJECT, 0, + G_IMPLEMENT_INTERFACE (SP_TYPE_SOURCE, source_iface_init)) + +SpSource * +sp_hostinfo_source_new (void) +{ + return g_object_new (SP_TYPE_HOSTINFO_SOURCE, NULL); +} + +static void +poll_cpu (SpHostinfoSource *self) +{ + gchar cpu[64] = { 0 }; + glong user; + glong sys; + glong nice; + glong idle; + glong iowait; + glong irq; + glong softirq; + glong steal; + glong guest; + glong guest_nice; + glong user_calc; + glong system_calc; + glong nice_calc; + glong idle_calc; + glong iowait_calc; + glong irq_calc; + glong softirq_calc; + glong steal_calc; + glong guest_calc; + glong guest_nice_calc; + gchar *buf = NULL; + glong total; + gchar *line; + gint ret; + gint id; + gint i; + + if (g_file_get_contents("/proc/stat", &buf, NULL, NULL)) + { + line = buf; + for (i = 0; buf[i]; i++) + { + if (buf[i] == '\n') { + buf[i] = '\0'; + if (g_str_has_prefix(line, "cpu")) + { + if (isdigit(line[3])) + { + CpuInfo *cpu_info; + + user = nice = sys = idle = id = 0; + ret = sscanf (line, "%s %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld", + cpu, &user, &nice, &sys, &idle, + &iowait, &irq, &softirq, &steal, &guest, &guest_nice); + if (ret != 11) + goto next; + + ret = sscanf(cpu, "cpu%d", &id); + + if (ret != 1 || id < 0 || id >= self->n_cpu) + goto next; + + cpu_info = &g_array_index (self->cpu_info, CpuInfo, id); + + user_calc = user - cpu_info->last_user; + nice_calc = nice - cpu_info->last_nice; + system_calc = sys - cpu_info->last_system; + idle_calc = idle - cpu_info->last_idle; + iowait_calc = iowait - cpu_info->last_iowait; + irq_calc = irq - cpu_info->last_irq; + softirq_calc = softirq - cpu_info->last_softirq; + steal_calc = steal - cpu_info->last_steal; + guest_calc = guest - cpu_info->last_guest; + guest_nice_calc = guest_nice - cpu_info->last_guest_nice; + + total = user_calc + nice_calc + system_calc + idle_calc + iowait_calc + irq_calc + softirq_calc + steal_calc + guest_calc + guest_nice_calc; + cpu_info->total = ((total - idle_calc) / (gdouble)total) * 100.0; + + cpu_info->last_user = user; + cpu_info->last_nice = nice; + cpu_info->last_idle = idle; + cpu_info->last_system = sys; + cpu_info->last_iowait = iowait; + cpu_info->last_irq = irq; + cpu_info->last_softirq = softirq; + cpu_info->last_steal = steal; + cpu_info->last_guest = guest; + cpu_info->last_guest_nice = guest_nice; + } + } else { + /* CPU info comes first. Skip further lines. */ + break; + } + + next: + line = &buf[i + 1]; + } + } + } + + g_free (buf); +} + +static void +publish_cpu (SpHostinfoSource *self) +{ + SpCaptureCounterValue *counter_values; + guint *counter_ids; + gint i; + + counter_ids = alloca (sizeof *counter_ids * self->n_cpu * 2); + counter_values = alloca (sizeof *counter_values * self->n_cpu * 2); + + for (i = 0; i < self->n_cpu; i++) + { + CpuInfo *info = &g_array_index (self->cpu_info, CpuInfo, i); + SpCaptureCounterValue *value = &counter_values[i*2]; + guint *id = &counter_ids[i*2]; + + *id = info->counter_base; + value->vdbl = info->total; + + id++; + value++; + + *id = info->counter_base + 1; + value->vdbl = info->freq; + } + + sp_capture_writer_set_counters (self->writer, + SP_CAPTURE_CURRENT_TIME, + getpid (), + -1, + counter_ids, + counter_values, + self->n_cpu * 2); +} + +static gboolean +collect_hostinfo_cb (gpointer data) +{ + SpHostinfoSource *self = data; + + g_assert (SP_IS_HOSTINFO_SOURCE (self)); + + poll_cpu (self); + publish_cpu (self); + + return G_SOURCE_CONTINUE; +} + +static void +sp_hostinfo_source_finalize (GObject *object) +{ + SpHostinfoSource *self = (SpHostinfoSource *)object; + + if (self->handler) + { + g_source_remove (self->handler); + self->handler = 0; + } + + g_clear_pointer (&self->writer, sp_capture_writer_unref); + g_clear_pointer (&self->cpu_info, g_array_unref); + + G_OBJECT_CLASS (sp_hostinfo_source_parent_class)->finalize (object); +} + +static void +sp_hostinfo_source_class_init (SpHostinfoSourceClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = sp_hostinfo_source_finalize; +} + +static void +sp_hostinfo_source_init (SpHostinfoSource *self) +{ + self->cpu_info = g_array_new (FALSE, TRUE, sizeof (CpuInfo)); +} + +static void +sp_hostinfo_source_set_writer (SpSource *source, + SpCaptureWriter *writer) +{ + SpHostinfoSource *self = (SpHostinfoSource *)source; + + g_assert (SP_IS_HOSTINFO_SOURCE (self)); + g_assert (writer != NULL); + + g_clear_pointer (&self->writer, sp_capture_writer_unref); + self->writer = sp_capture_writer_ref (writer); +} + +static void +sp_hostinfo_source_start (SpSource *source) +{ + SpHostinfoSource *self = (SpHostinfoSource *)source; + + g_assert (SP_IS_HOSTINFO_SOURCE (self)); + + self->handler = g_timeout_add (500, collect_hostinfo_cb, self); +} + +static void +sp_hostinfo_source_stop (SpSource *source) +{ + SpHostinfoSource *self = (SpHostinfoSource *)source; + + g_assert (SP_IS_HOSTINFO_SOURCE (self)); + + g_source_remove (self->handler); + self->handler = 0; + + sp_source_emit_finished (SP_SOURCE (self)); +} + +static void +sp_hostinfo_source_prepare (SpSource *source) +{ + SpHostinfoSource *self = (SpHostinfoSource *)source; + SpCaptureCounter *counters; + gint i; + + g_assert (SP_IS_HOSTINFO_SOURCE (self)); + + self->n_cpu = g_get_num_processors (); + + g_array_set_size (self->cpu_info, 0); + + counters = alloca (sizeof *counters * self->n_cpu * 2); + + for (i = 0; i < self->n_cpu; i++) + { + SpCaptureCounter *ctr = &counters[i*2]; + CpuInfo info = { 0 }; + + /* + * Request 2 counter values. + * One for CPU and one for Frequency. + */ + info.counter_base = sp_capture_writer_request_counter (self->writer, 2); + + /* + * Define counters for capture file. + */ + ctr->id = info.counter_base; + ctr->type = SP_CAPTURE_COUNTER_DOUBLE; + ctr->value.vdbl = 0; + g_strlcpy (ctr->category, "CPU Percent", sizeof ctr->category); + g_snprintf (ctr->name, sizeof ctr->name, "Total CPU %d", i); + g_snprintf (ctr->description, sizeof ctr->description, + "Total CPU usage %d", i); + + ctr++; + + ctr->id = info.counter_base + 1; + ctr->type = SP_CAPTURE_COUNTER_DOUBLE; + ctr->value.vdbl = 0; + g_strlcpy (ctr->category, "CPU Frequency", sizeof ctr->category); + g_snprintf (ctr->name, sizeof ctr->name, "CPU %d", i); + g_snprintf (ctr->description, sizeof ctr->description, + "Frequency of CPU %d", i); + + g_array_append_val (self->cpu_info, info); + } + + sp_capture_writer_define_counters (self->writer, + SP_CAPTURE_CURRENT_TIME, + getpid (), + -1, + counters, + self->n_cpu * 2); + + sp_source_emit_ready (SP_SOURCE (self)); +} + +static void +source_iface_init (SpSourceInterface *iface) +{ + iface->set_writer = sp_hostinfo_source_set_writer; + iface->prepare = sp_hostinfo_source_prepare; + iface->start = sp_hostinfo_source_start; + iface->stop = sp_hostinfo_source_stop; +} diff --git a/lib/sp-hostinfo-source.h b/lib/sp-hostinfo-source.h new file mode 100644 index 00000000..873dd115 --- /dev/null +++ b/lib/sp-hostinfo-source.h @@ -0,0 +1,35 @@ +/* sp-hostinfo-source.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_HOSTINFO_SOURCE_H +#define SP_HOSTINFO_SOURCE_H + +#include "sp-source.h" + +G_BEGIN_DECLS + +#define SP_TYPE_HOSTINFO_SOURCE (sp_hostinfo_source_get_type()) + +G_DECLARE_FINAL_TYPE (SpHostinfoSource, sp_hostinfo_source, SP, HOSTINFO_SOURCE, GObject) + +SpSource *sp_hostinfo_source_new (void); + +G_END_DECLS + +#endif /* SP_HOSTINFO_SOURCE_H */ + diff --git a/lib/sp-line-visualizer-row.c b/lib/sp-line-visualizer-row.c new file mode 100644 index 00000000..0ae5826d --- /dev/null +++ b/lib/sp-line-visualizer-row.c @@ -0,0 +1,396 @@ +/* sp-line-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 . + */ + +#include + +#include "sp-line-visualizer-row.h" + +typedef struct +{ + gint64 time; + SpCaptureCounterValue value; +} LineDataItem; + +typedef struct +{ + guint id; + guint type; + GArray *values; +} LineData; + +struct _SpLineVisualizerRow +{ + 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) + +enum { + PROP_0, + PROP_TITLE, + N_PROPS +}; + +static GParamSpec *properties [N_PROPS]; + +static void +line_data_free (gpointer data) +{ + LineData *ld = data; + + if (ld != NULL) + { + g_array_unref (ld->values); + g_free (ld); + } +} + +static void +sp_line_visualizer_row_recalc (SpLineVisualizerRow *self) +{ + GHashTable *new_data; + SpCaptureFrame frame; + + g_assert (SP_IS_LINE_VISUALIZER_ROW (self)); + + self->needs_recalc = FALSE; + + if (self->reader == NULL) + return; + + sp_capture_reader_reset (self->reader); + + self->start_time = sp_capture_reader_get_start_time (self->reader); + self->duration = 0; + + new_data = g_hash_table_new_full (NULL, NULL, NULL, line_data_free); + + while (sp_capture_reader_peek_frame (self->reader, &frame)) + { + gint64 relative; + + if (frame.time < self->start_time) + frame.time = self->start_time; + + relative = frame.time - self->start_time; + if (relative > self->duration) + self->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))) + 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))) + { + LineData *ld = g_hash_table_lookup (new_data, GSIZE_TO_POINTER (ctr->id)); + LineDataItem item; + + if (ld == NULL) + { + ld = g_new (LineData, 1); + ld->id = ctr->id; + ld->type = ctr->type; + ld->values = g_array_new (FALSE, FALSE, sizeof (LineDataItem)); + 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.value = ctr->value; + + g_array_append_val (ld->values, item); + } + } + } + else if (frame.type == SP_CAPTURE_FRAME_CTRSET) + { + const SpCaptureFrameCounterSet *set; + LineDataItem item; + guint i; + + if (NULL == (set = sp_capture_reader_read_counter_set (self->reader))) + return; + + item.time = MAX (self->start_time, set->frame.time) - self->start_time; + + for (i = 0; i < set->n_values; i++) + { + const SpCaptureCounterValues *values = &set->values[i]; + guint j; + + for (j = 0; j < G_N_ELEMENTS (values->values); j++) + { + LineData *ld; + + if (values->ids[j] == 0) + break; + + ld = g_hash_table_lookup (new_data, GSIZE_TO_POINTER (values->ids[j])); + + /* possible there was no matching ctrdef */ + if (ld == NULL) + continue; + + item.value = values->values[j]; + + g_array_append_val (ld->values, item); + } + } + } + else if (!sp_capture_reader_skip (self->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; +} + +static inline gdouble +calc_y (SpLineVisualizerRow *self, + LineData *ld, + SpCaptureCounterValue value) +{ + gdouble ret = value.vdbl / 100.0; + return ret; +} + +static gboolean +sp_line_visualizer_row_draw (GtkWidget *widget, + cairo_t *cr) +{ + SpLineVisualizerRow *self = (SpLineVisualizerRow *)widget; + GtkAllocation alloc; + GHashTableIter iter; + gboolean ret; + gpointer key, val; + guint i; + + g_assert (SP_IS_LINE_VISUALIZER_ROW (widget)); + g_assert (cr != NULL); + + if (self->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); + + g_hash_table_iter_init (&iter, self->counters); + + while (g_hash_table_iter_next (&iter, &key, &val)) + { + LineData *ld = val; + gdouble last_x = 0; + gdouble last_y = 0; + + cairo_move_to (cr, 0, alloc.height); + + for (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); + + 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); + + return ret; +} + +static void +sp_line_visualizer_row_set_reader (SpVisualizerRow *row, + SpCaptureReader *reader) +{ + SpLineVisualizerRow *self = (SpLineVisualizerRow *)row; + + g_assert (SP_IS_LINE_VISUALIZER_ROW (self)); + + if (self->reader != reader) + { + g_clear_pointer (&self->reader, sp_capture_reader_unref); + + 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); + + gtk_widget_queue_draw (GTK_WIDGET (self)); + } +} + +static void +sp_line_visualizer_row_finalize (GObject *object) +{ + SpLineVisualizerRow *self = (SpLineVisualizerRow *)object; + + g_clear_pointer (&self->counters, g_hash_table_unref); + g_clear_pointer (&self->reader, sp_capture_reader_unref); + + G_OBJECT_CLASS (sp_line_visualizer_row_parent_class)->finalize (object); +} + +static void +sp_line_visualizer_row_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SpLineVisualizerRow *self = SP_LINE_VISUALIZER_ROW (object); + + switch (prop_id) + { + case PROP_TITLE: + g_object_get_property (G_OBJECT (self->label), "label", value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sp_line_visualizer_row_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SpLineVisualizerRow *self = SP_LINE_VISUALIZER_ROW (object); + + switch (prop_id) + { + case PROP_TITLE: + g_object_set_property (G_OBJECT (self->label), "label", value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sp_line_visualizer_row_class_init (SpLineVisualizerRowClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + SpVisualizerRowClass *visualizer_class = SP_VISUALIZER_ROW_CLASS (klass); + + 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; + + widget_class->draw = sp_line_visualizer_row_draw; + + visualizer_class->set_reader = sp_line_visualizer_row_set_reader; + + properties [PROP_TITLE] = + g_param_spec_string ("title", + "Title", + "The title of the row", + NULL, + (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); +} + +static void +sp_line_visualizer_row_init (SpLineVisualizerRow *self) +{ + PangoAttrList *attrs = NULL; + + self->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, + "attributes", attrs, + "visible", TRUE, + "xalign", 0.0f, + "yalign", 0.0f, + NULL); + gtk_container_add (GTK_CONTAINER (self), GTK_WIDGET (self->label)); + pango_attr_list_unref (attrs); +} + +void +sp_line_visualizer_row_add_counter (SpLineVisualizerRow *self, + guint counter_id) +{ + g_assert (SP_IS_LINE_VISUALIZER_ROW (self)); + + self->needs_recalc = TRUE; + g_hash_table_insert (self->counters, GSIZE_TO_POINTER (counter_id), NULL); + gtk_widget_queue_draw (GTK_WIDGET (self)); +} diff --git a/lib/sp-line-visualizer-row.h b/lib/sp-line-visualizer-row.h new file mode 100644 index 00000000..bf6aef83 --- /dev/null +++ b/lib/sp-line-visualizer-row.h @@ -0,0 +1,36 @@ +/* sp-line-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_LINE_VISUALIZER_ROW_H +#define SP_LINE_VISUALIZER_ROW_H + +#include "sp-visualizer-row.h" + +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) + +GtkWidget *sp_line_visualizer_row_new (void); +void sp_line_visualizer_row_add_counter (SpLineVisualizerRow *self, + guint counter_id); + +G_END_DECLS + +#endif /* SP_LINE_VISUALIZER_ROW_H */ diff --git a/lib/sp-visualizer-row.c b/lib/sp-visualizer-row.c new file mode 100644 index 00000000..c6018884 --- /dev/null +++ b/lib/sp-visualizer-row.c @@ -0,0 +1,41 @@ +/* sp-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 . + */ + +#include "sp-visualizer-row.h" + +G_DEFINE_ABSTRACT_TYPE (SpVisualizerRow, sp_visualizer_row, GTK_TYPE_LIST_BOX_ROW) + +static void +sp_visualizer_row_class_init (SpVisualizerRowClass *klass) +{ +} + +static void +sp_visualizer_row_init (SpVisualizerRow *self) +{ +} + +void +sp_visualizer_row_set_reader (SpVisualizerRow *self, + SpCaptureReader *reader) +{ + g_return_if_fail (SP_IS_VISUALIZER_ROW (self)); + + if (SP_VISUALIZER_ROW_GET_CLASS (self)->set_reader) + SP_VISUALIZER_ROW_GET_CLASS (self)->set_reader (self, reader); +} diff --git a/lib/sp-visualizer-row.h b/lib/sp-visualizer-row.h new file mode 100644 index 00000000..137fdd8b --- /dev/null +++ b/lib/sp-visualizer-row.h @@ -0,0 +1,52 @@ +/* sp-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_VISUALIZER_ROW_H +#define SP_VISUALIZER_ROW_H + +#include + +#include "sp-capture-reader.h" + +G_BEGIN_DECLS + +#define SP_TYPE_VISUALIZER_ROW (sp_visualizer_row_get_type()) + +G_DECLARE_DERIVABLE_TYPE (SpVisualizerRow, sp_visualizer_row, SP, VISUALIZER_ROW, GtkListBoxRow) + +struct _SpVisualizerRowClass +{ + GtkListBoxRowClass parent_class; + + /** + * SpVisualizerRow::set_reader: + * + * Sets the reader that the row should use to extract counters. + * This reader is private to the row and should be freed when + * no longer in use with sp_capture_reader_unref(). + */ + void (*set_reader) (SpVisualizerRow *self, + SpCaptureReader *reader); +}; + +void sp_visualizer_row_set_reader (SpVisualizerRow *self, + SpCaptureReader *reader); + +G_END_DECLS + +#endif /* SP_VISUALIZER_ROW_H */ diff --git a/lib/sysprof-ui.h b/lib/sysprof-ui.h index d215241e..ea290901 100644 --- a/lib/sysprof-ui.h +++ b/lib/sysprof-ui.h @@ -26,6 +26,7 @@ G_BEGIN_DECLS #define SYSPROF_INSIDE # include "sp-callgraph-view.h" # include "sp-cell-renderer-percent.h" +# include "sp-line-visualizer-row.h" # include "sp-empty-state-view.h" # include "sp-model-filter.h" # include "sp-recording-state-view.h" @@ -33,6 +34,7 @@ G_BEGIN_DECLS # include "sp-process-model-item.h" # include "sp-process-model-row.h" # include "sp-profiler-menu-button.h" +# include "sp-visualizer-row.h" #undef SYSPROF_INSIDE G_END_DECLS diff --git a/lib/sysprof.h b/lib/sysprof.h index 0ff7db7b..30e17c92 100644 --- a/lib/sysprof.h +++ b/lib/sysprof.h @@ -32,6 +32,7 @@ G_BEGIN_DECLS # include "sp-elf-symbol-resolver.h" # include "sp-error.h" # include "sp-gjs-source.h" +# include "sp-hostinfo-source.h" # include "sp-jitmap-symbol-resolver.h" # include "sp-kernel-symbol-resolver.h" # include "sp-kernel-symbol.h" diff --git a/src/resources/theme/shared.css b/src/resources/theme/shared.css index b563d922..927d05b3 100644 --- a/src/resources/theme/shared.css +++ b/src/resources/theme/shared.css @@ -1,10 +1,14 @@ GtkPopover GtkListBox GtkListBoxRow, +GtkPaned GtkListBox GtkListBoxRow, +paned list row, popover list row { padding: 6px 10px 6px 10px; border-bottom: 1px solid alpha(@borders, 0.2); } GtkPopover GtkListBox GtkListBoxRow:last-child, +GtkPaned GtkListBox GtkListBoxRow:last-child, +paned list row:last-child, popover list row:last-child { border-bottom: none; } diff --git a/src/resources/ui/sp-window.ui b/src/resources/ui/sp-window.ui index 48df3fd7..1949bde7 100644 --- a/src/resources/ui/sp-window.ui +++ b/src/resources/ui/sp-window.ui @@ -160,8 +160,34 @@ - + + vertical true + + + 75 + 225 + true + + + true + + + CPU + 75 + false + true + + + + + + + + + true + + browsing diff --git a/src/sp-window.c b/src/sp-window.c index 3907b853..fa476832 100644 --- a/src/sp-window.c +++ b/src/sp-window.c @@ -48,6 +48,7 @@ struct _SpWindow GtkLabel *stat_label; GtkLabel *title; GtkStack *view_stack; + GtkListBox *visualizer_rows; guint stats_handler; @@ -211,12 +212,16 @@ sp_window_build_profile_cb (GObject *object, } sp_callgraph_view_set_profile (self->callgraph_view, SP_CALLGRAPH_PROFILE (profile)); - sp_window_set_state (self, SP_WINDOW_STATE_BROWSING); - if (sp_callgraph_view_get_n_functions (self->callgraph_view) == 0) sp_window_notify_user (self, GTK_MESSAGE_WARNING, _("Not enough samples were collected to generate a callgraph")); + + gtk_container_foreach (GTK_CONTAINER (self->visualizer_rows), + (GtkCallback)sp_visualizer_row_set_reader, + self->reader); + + sp_window_set_state (self, SP_WINDOW_STATE_BROWSING); } static void @@ -362,6 +367,7 @@ static void sp_window_add_sources (SpWindow *window, SpProfiler *profiler) { + g_autoptr(SpSource) host_source = NULL; g_autoptr(SpSource) proc_source = NULL; g_autoptr(SpSource) perf_source = NULL; @@ -373,6 +379,9 @@ sp_window_add_sources (SpWindow *window, perf_source = sp_perf_source_new (); sp_profiler_add_source (profiler, perf_source); + + host_source = sp_hostinfo_source_new (); + sp_profiler_add_source (profiler, host_source); } static void @@ -760,6 +769,7 @@ sp_window_class_init (SpWindowClass *klass) gtk_widget_class_bind_template_child (widget_class, SpWindow, subtitle); gtk_widget_class_bind_template_child (widget_class, SpWindow, title); gtk_widget_class_bind_template_child (widget_class, SpWindow, view_stack); + gtk_widget_class_bind_template_child (widget_class, SpWindow, visualizer_rows); } static void diff --git a/tools/sysprof-cli.c b/tools/sysprof-cli.c index 3cbbddd0..9e24415c 100644 --- a/tools/sysprof-cli.c +++ b/tools/sysprof-cli.c @@ -190,7 +190,10 @@ main (gint argc, g_object_unref (source); source = sp_perf_source_new (); + sp_profiler_add_source (profiler, source); + g_object_unref (source); + source = sp_hostinfo_source_new (); sp_profiler_add_source (profiler, source); g_object_unref (source);