From 6d4796294e15432657294b0f0b652ea39364dcf5 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 26 Jun 2019 18:58:31 -0700 Subject: [PATCH] netdev: start on netdev aid to display network graphs --- src/libsysprof-ui/meson.build | 2 + src/libsysprof-ui/sysprof-display.c | 2 + src/libsysprof-ui/sysprof-duplex-visualizer.c | 554 ++++++++++++++++++ src/libsysprof-ui/sysprof-duplex-visualizer.h | 39 ++ src/libsysprof-ui/sysprof-line-visualizer.c | 2 +- src/libsysprof-ui/sysprof-netdev-aid.c | 259 ++++++++ src/libsysprof-ui/sysprof-netdev-aid.h | 33 ++ .../sysprof-profiler-assistant.c | 10 +- .../sysprof-profiler-assistant.ui | 8 +- src/libsysprof/sysprof-netdev-source.c | 2 +- 10 files changed, 898 insertions(+), 13 deletions(-) create mode 100644 src/libsysprof-ui/sysprof-duplex-visualizer.c create mode 100644 src/libsysprof-ui/sysprof-duplex-visualizer.h create mode 100644 src/libsysprof-ui/sysprof-netdev-aid.c create mode 100644 src/libsysprof-ui/sysprof-netdev-aid.h diff --git a/src/libsysprof-ui/meson.build b/src/libsysprof-ui/meson.build index 69ec5799..6cd75a94 100644 --- a/src/libsysprof-ui/meson.build +++ b/src/libsysprof-ui/meson.build @@ -29,6 +29,7 @@ libsysprof_ui_private_sources = [ 'sysprof-depth-visualizer.c', 'sysprof-details-page.c', 'sysprof-display.c', + 'sysprof-duplex-visualizer.c', 'sysprof-environ.c', 'sysprof-environ-editor.c', 'sysprof-environ-editor-row.c', @@ -43,6 +44,7 @@ libsysprof_ui_private_sources = [ 'sysprof-marks-page.c', 'sysprof-mark-visualizer.c', 'sysprof-memory-aid.c', + 'sysprof-netdev-aid.c', 'sysprof-profiler-assistant.c', 'sysprof-proxy-aid.c', 'sysprof-recording-state-view.c', diff --git a/src/libsysprof-ui/sysprof-display.c b/src/libsysprof-ui/sysprof-display.c index d9dbf24f..6e4d7539 100644 --- a/src/libsysprof-ui/sysprof-display.c +++ b/src/libsysprof-ui/sysprof-display.c @@ -41,6 +41,7 @@ #include "sysprof-cpu-aid.h" #include "sysprof-logs-aid.h" #include "sysprof-marks-aid.h" +#include "sysprof-netdev-aid.h" typedef enum { @@ -632,6 +633,7 @@ sysprof_display_present_async (SysprofDisplay *self, g_ptr_array_add (aids, sysprof_callgraph_aid_new ()); g_ptr_array_add (aids, sysprof_logs_aid_new ()); g_ptr_array_add (aids, sysprof_marks_aid_new ()); + g_ptr_array_add (aids, sysprof_netdev_aid_new ()); task = g_task_new (self, cancellable, callback, user_data); g_task_set_source_tag (task, sysprof_display_present_async); diff --git a/src/libsysprof-ui/sysprof-duplex-visualizer.c b/src/libsysprof-ui/sysprof-duplex-visualizer.c new file mode 100644 index 00000000..bed78595 --- /dev/null +++ b/src/libsysprof-ui/sysprof-duplex-visualizer.c @@ -0,0 +1,554 @@ +/* sysprof-duplex-visualizer.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-duplex-visualizer" + +#include "config.h" + +#include "pointcache.h" +#include "sysprof-duplex-visualizer.h" + +#define LABEL_HEIGHT_PX 10 + +struct _SysprofDuplexVisualizer +{ + SysprofVisualizer parent_instance; + + gint64 begin_time; + gint64 duration; + + guint rx_counter; + guint tx_counter; + + GdkRGBA rx_rgba; + GdkRGBA tx_rgba; + + guint rx_rgba_set : 1; + guint tx_rgba_set : 1; + + PointCache *cache; +}; + +typedef struct +{ + PointCache *cache; + + gint64 begin_time; + gint64 duration; + + gint64 max_change; + + /* Last value to convert to rate of change */ + gint64 last_rx_val; + gint64 last_tx_val; + + /* Counter IDs */ + guint rx; + guint tx; +} Collect; + +G_DEFINE_TYPE (SysprofDuplexVisualizer, sysprof_duplex_visualizer, SYSPROF_TYPE_VISUALIZER) + +static gboolean +collect_ranges_cb (const SysprofCaptureFrame *frame, + gpointer data) +{ + Collect *state = data; + + g_assert (frame != NULL); + g_assert (state != NULL); + g_assert (state->cache != NULL); + + if (frame->type == SYSPROF_CAPTURE_FRAME_CTRSET) + { + const SysprofCaptureCounterSet *set = (gconstpointer)frame; + + for (guint i = 0; i < set->n_values; i++) + { + const SysprofCaptureCounterValues *values = &set->values[i]; + + for (guint j = 0; j < G_N_ELEMENTS (values->ids); j++) + { + gint64 v64 = values->values[j].v64; + guint id = values->ids[j]; + gint64 max_change = 0; + + if (id == 0) + break; + + if (id == state->rx) + { + if (state->last_rx_val != G_MININT64) + max_change = v64 - state->last_rx_val; + state->last_rx_val = v64; + } + else if (id == state->tx) + { + if (state->last_tx_val != G_MININT64) + max_change = v64 - state->last_tx_val; + state->last_tx_val = v64; + } + else + { + continue; + } + + if (max_change > state->max_change) + state->max_change = max_change; + } + } + } + + return TRUE; +} + +static gboolean +collect_values_cb (const SysprofCaptureFrame *frame, + gpointer data) +{ + Collect *state = data; + + g_assert (frame != NULL); + g_assert (state != NULL); + g_assert (state->cache != NULL); + + if (frame->type == SYSPROF_CAPTURE_FRAME_CTRSET) + { + const SysprofCaptureCounterSet *set = (gconstpointer)frame; + gdouble x = (frame->time - state->begin_time) / (gdouble)state->duration; + + for (guint i = 0; i < set->n_values; i++) + { + const SysprofCaptureCounterValues *values = &set->values[i]; + + for (guint j = 0; j < G_N_ELEMENTS (values->ids); j++) + { + gint64 v64 = values->values[j].v64; + guint id = values->ids[j]; + gint64 val = 0; + gdouble y = 0.5; + + if (id == 0) + break; + + if (id == state->rx) + { + if (state->last_rx_val != G_MININT64) + val = v64 - state->last_rx_val; + + /* RX goes upward from half point */ + if (state->max_change != 0) + y += (gdouble)val / (gdouble)state->max_change / 2.0; + + state->last_rx_val = v64; + } + else if (id == state->tx) + { + if (state->last_tx_val != G_MININT64) + val = v64 - state->last_tx_val; + + /* RX goes upward from half point */ + if (state->max_change != 0) + y += (gdouble)val / (gdouble)state->max_change / 2.0; + + state->last_tx_val = v64; + } + else + { + continue; + } + + point_cache_add_point_to_set (state->cache, id, x, y); + } + } + } + + return TRUE; +} + +static void +sysprof_duplex_visualizer_worker (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) +{ + SysprofCaptureCursor *cursor = task_data; + SysprofDuplexVisualizer *self = source_object; + Collect state = {0}; + + g_assert (G_IS_TASK (task)); + g_assert (SYSPROF_IS_DUPLEX_VISUALIZER (self)); + g_assert (cursor != NULL); + g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); + + state.cache = point_cache_new (); + state.begin_time = self->begin_time; + state.duration = self->duration; + state.rx = g_atomic_int_get (&self->rx_counter); + state.tx = g_atomic_int_get (&self->tx_counter); + state.last_rx_val = G_MININT64; + state.last_tx_val = G_MININT64; + state.max_change = 0; + + point_cache_add_set (state.cache, state.rx); + point_cache_add_set (state.cache, state.tx); + + sysprof_capture_cursor_foreach (cursor, collect_ranges_cb, &state); + sysprof_capture_cursor_reset (cursor); + + /* Give just a bit of overhead */ + state.max_change *= 1.1; + + /* Reset for calculations */ + state.last_rx_val = G_MININT64; + state.last_tx_val = G_MININT64; + + sysprof_capture_cursor_foreach (cursor, collect_values_cb, &state); + + g_task_return_pointer (task, + g_steal_pointer (&state.cache), + (GDestroyNotify) point_cache_unref); +} + +static void +load_data_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + SysprofDuplexVisualizer *self = (SysprofDuplexVisualizer *)object; + g_autoptr(PointCache) pc = NULL; + + g_assert (SYSPROF_IS_DUPLEX_VISUALIZER (self)); + g_assert (G_IS_TASK (result)); + + if ((pc = g_task_propagate_pointer (G_TASK (result), NULL))) + { + g_clear_pointer (&self->cache, point_cache_unref); + self->cache = g_steal_pointer (&pc); + gtk_widget_queue_draw (GTK_WIDGET (self)); + } +} + +static void +sysprof_duplex_visualizer_set_reader (SysprofVisualizer *visualizer, + SysprofCaptureReader *reader) +{ + SysprofDuplexVisualizer *self = (SysprofDuplexVisualizer *)visualizer; + g_autoptr(SysprofCaptureCursor) cursor = NULL; + g_autoptr(GTask) task = NULL; + SysprofCaptureCondition *c; + guint counters[2]; + + g_assert (SYSPROF_IS_DUPLEX_VISUALIZER (self)); + g_assert (reader != NULL); + + self->begin_time = sysprof_capture_reader_get_start_time (reader); + self->duration = sysprof_capture_reader_get_end_time (reader) + - sysprof_capture_reader_get_start_time (reader); + + counters[0] = self->rx_counter; + counters[1] = self->tx_counter; + + cursor = sysprof_capture_cursor_new (reader); + c = sysprof_capture_condition_new_where_counter_in (G_N_ELEMENTS (counters), counters); + sysprof_capture_cursor_add_condition (cursor, g_steal_pointer (&c)); + + task = g_task_new (self, NULL, load_data_cb, NULL); + g_task_set_source_tag (task, sysprof_duplex_visualizer_set_reader); + g_task_set_task_data (task, + g_steal_pointer (&cursor), + (GDestroyNotify)sysprof_capture_cursor_unref); + g_task_run_in_thread (task, sysprof_duplex_visualizer_worker); +} + +static gboolean +sysprof_duplex_visualizer_draw (GtkWidget *widget, + cairo_t *cr) +{ + static const gdouble dashes[] = { 1.0, 2.0 }; + SysprofDuplexVisualizer *self = (SysprofDuplexVisualizer *)widget; + PangoFontDescription *font_desc; + GtkStyleContext *style_context; + PangoLayout *layout; + GtkAllocation alloc; + GdkRectangle clip; + gboolean ret; + GdkRGBA fg; + guint mid; + + g_assert (SYSPROF_IS_DUPLEX_VISUALIZER (self)); + g_assert (cr != NULL); + + gtk_widget_get_allocation (widget, &alloc); + gdk_cairo_get_clip_rectangle (cr, &clip); + + mid = alloc.height / 2; + + ret = GTK_WIDGET_CLASS (sysprof_duplex_visualizer_parent_class)->draw (widget, cr); + + /* Draw our center line */ + cairo_save (cr); + cairo_set_line_width (cr, 1.0); + cairo_set_dash (cr, dashes, G_N_ELEMENTS (dashes), 0); + cairo_move_to (cr, 0, mid); + cairo_line_to (cr, alloc.width, mid); + cairo_stroke (cr); + cairo_restore (cr); + + if (self->cache != NULL) + { + g_autofree SysprofVisualizerAbsolutePoint *points = NULL; + const Point *fpoints; + guint n_fpoints = 0; + + cairo_save (cr); + cairo_set_line_width (cr, 1.0); + if (self->rx_rgba_set) + gdk_cairo_set_source_rgba (cr, &self->rx_rgba); + + fpoints = point_cache_get_points (self->cache, self->rx_counter, &n_fpoints); + + if (n_fpoints > 0) + { + GdkRGBA rgba = self->rx_rgba; + gdouble last_x; + gdouble last_y; + guint p; + + points = g_realloc_n (points, n_fpoints, sizeof *points); + + sysprof_visualizer_translate_points (SYSPROF_VISUALIZER (self), + (const SysprofVisualizerRelativePoint *)fpoints, + n_fpoints, + points, + n_fpoints); + + /* Skip past data that we won't see anyway */ + for (p = 0; p < n_fpoints; p++) + { + if (points[p].x >= clip.x) + break; + } + + if (p >= n_fpoints) + return ret; + + /* But get at least one data point to anchor out of view */ + if (p > 0) + p--; + + last_x = points[p].x; + last_y = points[p].y; + + cairo_move_to (cr, last_x, mid); + cairo_line_to (cr, last_x, last_y); + + for (guint i = p + 1; i < n_fpoints; i++) + { + cairo_curve_to (cr, + last_x + ((points[i].x - last_x) / 2), + last_y, + last_x + ((points[i].x - last_x) / 2), + points[i].y, + points[i].x, + points[i].y); + + last_x = points[i].x; + last_y = points[i].y; + + if (points[i].x > clip.x + clip.width) + break; + } + + cairo_line_to (cr, last_x, mid); + cairo_close_path (cr); + cairo_stroke_preserve (cr); + rgba.alpha *= 0.5; + gdk_cairo_set_source_rgba (cr, &rgba); + cairo_fill (cr); + } + + cairo_restore (cr); + + /* AND NOW Tx */ + + cairo_save (cr); + cairo_set_line_width (cr, 1.0); + if (self->tx_rgba_set) + gdk_cairo_set_source_rgba (cr, &self->tx_rgba); + + fpoints = point_cache_get_points (self->cache, self->tx_counter, &n_fpoints); + + if (n_fpoints > 0) + { + GdkRGBA rgba = self->tx_rgba; + gdouble last_x; + gdouble last_y; + guint p; + + points = g_realloc_n (points, n_fpoints, sizeof *points); + + sysprof_visualizer_translate_points (SYSPROF_VISUALIZER (self), + (const SysprofVisualizerRelativePoint *)fpoints, + n_fpoints, + points, + n_fpoints); + + /* Skip past data that we won't see anyway */ + for (p = 0; p < n_fpoints; p++) + { + if (points[p].x >= clip.x) + break; + } + + if (p >= n_fpoints) + return ret; + + /* But get at least one data point to anchor out of view */ + if (p > 0) + p--; + + last_x = points[p].x; + last_y = points[p].y; + + cairo_move_to (cr, last_x, mid); + cairo_line_to (cr, last_x, last_y); + + for (guint i = p + 1; i < n_fpoints; i++) + { + cairo_curve_to (cr, + last_x + ((points[i].x - last_x) / 2), + last_y, + last_x + ((points[i].x - last_x) / 2), + points[i].y, + points[i].x, + points[i].y); + + last_x = points[i].x; + last_y = points[i].y; + + if (points[i].x > clip.x + clip.width) + break; + } + + cairo_line_to (cr, last_x, mid); + cairo_close_path (cr); + cairo_stroke_preserve (cr); + rgba.alpha *= 0.5; + gdk_cairo_set_source_rgba (cr, &rgba); + cairo_fill (cr); + } + + cairo_restore (cr); + } + + layout = gtk_widget_create_pango_layout (widget, ""); + + font_desc = pango_font_description_new (); + pango_font_description_set_family_static (font_desc, "Monospace"); + pango_font_description_set_absolute_size (font_desc, LABEL_HEIGHT_PX * PANGO_SCALE); + pango_layout_set_font_description (layout, font_desc); + + style_context = gtk_widget_get_style_context (widget); + gtk_style_context_get_color (style_context, + gtk_style_context_get_state (style_context), + &fg); + fg.alpha = 0.5; + gdk_cairo_set_source_rgba (cr, &fg); + + cairo_move_to (cr, 2, 2); + pango_layout_set_text (layout, "RX", 2); + pango_cairo_show_layout (cr, layout); + + cairo_move_to (cr, 2, mid + 2); + pango_layout_set_text (layout, "TX", 2); + pango_cairo_show_layout (cr, layout); + + pango_font_description_free (font_desc); + g_object_unref (layout); + + return ret; +} + +static void +sysprof_duplex_visualizer_finalize (GObject *object) +{ + SysprofDuplexVisualizer *self = (SysprofDuplexVisualizer *)object; + + g_clear_pointer (&self->cache, point_cache_unref); + + G_OBJECT_CLASS (sysprof_duplex_visualizer_parent_class)->finalize (object); +} + +static void +sysprof_duplex_visualizer_class_init (SysprofDuplexVisualizerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + SysprofVisualizerClass *visualizer_class = SYSPROF_VISUALIZER_CLASS (klass); + + object_class->finalize = sysprof_duplex_visualizer_finalize; + + widget_class->draw = sysprof_duplex_visualizer_draw; + + visualizer_class->set_reader = sysprof_duplex_visualizer_set_reader; +} + +static void +sysprof_duplex_visualizer_init (SysprofDuplexVisualizer *self) +{ +} + +GtkWidget * +sysprof_duplex_visualizer_new (void) +{ + return g_object_new (SYSPROF_TYPE_DUPLEX_VISUALIZER, NULL); +} + +void +sysprof_duplex_visualizer_set_counters (SysprofDuplexVisualizer *self, + guint rx_counter, + guint tx_counter) +{ + g_return_if_fail (SYSPROF_IS_DUPLEX_VISUALIZER (self)); + g_return_if_fail (rx_counter != 0); + g_return_if_fail (tx_counter != 0); + + self->rx_counter = rx_counter; + self->tx_counter = tx_counter; +} + +void +sysprof_duplex_visualizer_set_colors (SysprofDuplexVisualizer *self, + const GdkRGBA *rx_rgba, + const GdkRGBA *tx_rgba) +{ + g_return_if_fail (SYSPROF_IS_DUPLEX_VISUALIZER (self)); + + if (rx_rgba) + self->rx_rgba = *rx_rgba; + self->rx_rgba_set = !!rx_rgba; + + if (tx_rgba) + self->tx_rgba = *tx_rgba; + self->tx_rgba_set = !!tx_rgba; + + gtk_widget_queue_draw (GTK_WIDGET (self)); +} diff --git a/src/libsysprof-ui/sysprof-duplex-visualizer.h b/src/libsysprof-ui/sysprof-duplex-visualizer.h new file mode 100644 index 00000000..19483eb8 --- /dev/null +++ b/src/libsysprof-ui/sysprof-duplex-visualizer.h @@ -0,0 +1,39 @@ +/* sysprof-duplex-visualizer.h + * + * 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 + */ + +#pragma once + +#include "sysprof-visualizer.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_DUPLEX_VISUALIZER (sysprof_duplex_visualizer_get_type()) + +G_DECLARE_FINAL_TYPE (SysprofDuplexVisualizer, sysprof_duplex_visualizer, SYSPROF, DUPLEX_VISUALIZER, SysprofVisualizer) + +GtkWidget *sysprof_duplex_visualizer_new (void); +void sysprof_duplex_visualizer_set_counters (SysprofDuplexVisualizer *self, + guint rx_counter, + guint tx_counter); +void sysprof_duplex_visualizer_set_colors (SysprofDuplexVisualizer *self, + const GdkRGBA *rx_rgba, + const GdkRGBA *tx_rgba); + +G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-line-visualizer.c b/src/libsysprof-ui/sysprof-line-visualizer.c index 1b0295e4..c23e8de7 100644 --- a/src/libsysprof-ui/sysprof-line-visualizer.c +++ b/src/libsysprof-ui/sysprof-line-visualizer.c @@ -109,7 +109,7 @@ enum { }; static GParamSpec *properties [N_PROPS]; -static gdouble dashes[] = { 1.0, 2.0 }; +static const gdouble dashes[] = { 1.0, 2.0 }; static void load_data_free (gpointer data) diff --git a/src/libsysprof-ui/sysprof-netdev-aid.c b/src/libsysprof-ui/sysprof-netdev-aid.c new file mode 100644 index 00000000..fe198955 --- /dev/null +++ b/src/libsysprof-ui/sysprof-netdev-aid.c @@ -0,0 +1,259 @@ +/* sysprof-netdev-aid.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-netdev-aid" + +#include "config.h" + +#include + +#include "sysprof-color-cycle.h" +#include "sysprof-duplex-visualizer.h" +#include "sysprof-netdev-aid.h" + +struct _SysprofNetdevAid +{ + SysprofAid parent_instance; +}; + +typedef struct +{ + SysprofCaptureCursor *cursor; + SysprofDisplay *display; +} Present; + +G_DEFINE_TYPE (SysprofNetdevAid, sysprof_netdev_aid, SYSPROF_TYPE_AID) + +static void +present_free (gpointer data) +{ + Present *p = data; + + g_clear_pointer (&p->cursor, sysprof_capture_cursor_unref); + g_clear_object (&p->display); + g_slice_free (Present, p); +} + +/** + * sysprof_netdev_aid_new: + * + * Create a new #SysprofNetdevAid. + * + * Returns: (transfer full): a newly created #SysprofNetdevAid + * + * Since: 3.34 + */ +SysprofAid * +sysprof_netdev_aid_new (void) +{ + return g_object_new (SYSPROF_TYPE_NETDEV_AID, NULL); +} + +static void +sysprof_netdev_aid_prepare (SysprofAid *self, + SysprofProfiler *profiler) +{ + g_autoptr(SysprofSource) source = NULL; + + g_assert (SYSPROF_IS_NETDEV_AID (self)); + g_assert (SYSPROF_IS_PROFILER (profiler)); + + source = sysprof_netdev_source_new (); + sysprof_profiler_add_source (profiler, source); +} + +static gboolean +collect_netdev_counters (const SysprofCaptureFrame *frame, + gpointer user_data) +{ + SysprofCaptureCounterDefine *def = (SysprofCaptureCounterDefine *)frame; + GArray *counters = user_data; + + g_assert (frame != NULL); + g_assert (frame->type == SYSPROF_CAPTURE_FRAME_CTRDEF); + g_assert (counters != NULL); + + for (guint i = 0; i < def->n_counters; i++) + { + const SysprofCaptureCounter *counter = &def->counters[i]; + + if (strcmp (counter->category, "Network") == 0 && + (g_str_has_prefix (counter->name, "RX Bytes") || + g_str_has_prefix (counter->name, "TX Bytes"))) + g_array_append_vals (counters, counter, 1); + } + + return TRUE; +} + +static void +sysprof_netdev_aid_present_worker (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) +{ + Present *present = task_data; + g_autoptr(GArray) counters = NULL; + + g_assert (G_IS_TASK (task)); + g_assert (SYSPROF_IS_NETDEV_AID (source_object)); + g_assert (present != NULL); + g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); + + counters = g_array_new (FALSE, FALSE, sizeof (SysprofCaptureCounter)); + sysprof_capture_cursor_foreach (present->cursor, collect_netdev_counters, counters); + g_task_return_pointer (task, + g_steal_pointer (&counters), + (GDestroyNotify) g_array_unref); +} + +static guint +find_other_id (GArray *counters, + const gchar *rx) +{ + g_autofree gchar *other = NULL; + + g_assert (counters); + g_assert (rx != NULL); + + other = g_strdup (rx); + other[0] = 'T'; + + for (guint i = 0; i < counters->len; i++) + { + const SysprofCaptureCounter *c = &g_array_index (counters, SysprofCaptureCounter, i); + + if (g_str_equal (c->name, other)) + return c->id; + } + + return 0; +} + +static void +sysprof_netdev_aid_present_async (SysprofAid *aid, + SysprofCaptureReader *reader, + SysprofDisplay *display, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + static const SysprofCaptureFrameType types[] = { SYSPROF_CAPTURE_FRAME_CTRDEF }; + g_autoptr(SysprofCaptureCondition) condition = NULL; + g_autoptr(SysprofCaptureCursor) cursor = NULL; + g_autoptr(GTask) task = NULL; + Present present; + + g_assert (SYSPROF_IS_NETDEV_AID (aid)); + g_assert (reader != NULL); + g_assert (SYSPROF_IS_DISPLAY (display)); + g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); + + condition = sysprof_capture_condition_new_where_type_in (1, types); + cursor = sysprof_capture_cursor_new (reader); + sysprof_capture_cursor_add_condition (cursor, g_steal_pointer (&condition)); + + present.cursor = g_steal_pointer (&cursor); + present.display = g_object_ref (display); + + task = g_task_new (aid, cancellable, callback, user_data); + g_task_set_source_tag (task, sysprof_netdev_aid_present_async); + g_task_set_task_data (task, + g_slice_dup (Present, &present), + present_free); + g_task_run_in_thread (task, sysprof_netdev_aid_present_worker); +} + +static gboolean +sysprof_netdev_aid_present_finish (SysprofAid *aid, + GAsyncResult *result, + GError **error) +{ + g_autoptr(GArray) counters = NULL; + Present *present; + + g_assert (SYSPROF_IS_AID (aid)); + g_assert (G_IS_TASK (result)); + + present = g_task_get_task_data (G_TASK (result)); + + if ((counters = g_task_propagate_pointer (G_TASK (result), error))) + { + g_autoptr(SysprofColorCycle) cycle = sysprof_color_cycle_new (); + SysprofVisualizerGroup *group; + + group = g_object_new (SYSPROF_TYPE_VISUALIZER_GROUP, + "can-focus", TRUE, + "title", _("Network"), + "visible", TRUE, + NULL); + + for (guint i = 0; i < counters->len; i++) + { + const SysprofCaptureCounter *ctr = &g_array_index (counters, SysprofCaptureCounter, i); + + if (g_str_has_prefix (ctr->name, "RX Bytes")) + { + g_autofree gchar *title = NULL; + GtkWidget *row; + GdkRGBA rgba; + guint other_id; + + if (!(other_id = find_other_id (counters, ctr->name))) + continue; + + title = g_strdup_printf ("Network Bytes%s", ctr->name + strlen ("RX Bytes")); + row = g_object_new (SYSPROF_TYPE_DUPLEX_VISUALIZER, + "title", title, + "height-request", 35, + "visible", TRUE, + NULL); + sysprof_color_cycle_next (cycle, &rgba); + sysprof_duplex_visualizer_set_counters (SYSPROF_DUPLEX_VISUALIZER (row), ctr->id, other_id); + sysprof_duplex_visualizer_set_colors (SYSPROF_DUPLEX_VISUALIZER (row), &rgba, &rgba); + sysprof_visualizer_group_insert (group, SYSPROF_VISUALIZER (row), -1, TRUE); + } + } + + if (counters->len > 0) + sysprof_display_add_group (present->display, group); + else + gtk_widget_destroy (GTK_WIDGET (group)); + } + + return counters != NULL; +} + +static void +sysprof_netdev_aid_class_init (SysprofNetdevAidClass *klass) +{ + SysprofAidClass *aid_class = SYSPROF_AID_CLASS (klass); + + aid_class->prepare = sysprof_netdev_aid_prepare; + aid_class->present_async = sysprof_netdev_aid_present_async; + aid_class->present_finish = sysprof_netdev_aid_present_finish; +} + +static void +sysprof_netdev_aid_init (SysprofNetdevAid *self) +{ + sysprof_aid_set_display_name (SYSPROF_AID (self), _("Network")); + sysprof_aid_set_icon_name (SYSPROF_AID (self), "preferences-system-network-symbolic"); +} diff --git a/src/libsysprof-ui/sysprof-netdev-aid.h b/src/libsysprof-ui/sysprof-netdev-aid.h new file mode 100644 index 00000000..6bff1765 --- /dev/null +++ b/src/libsysprof-ui/sysprof-netdev-aid.h @@ -0,0 +1,33 @@ +/* sysprof-netdev-aid.h + * + * 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 + */ + +#pragma once + +#include "sysprof-aid.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_NETDEV_AID (sysprof_netdev_aid_get_type()) + +G_DECLARE_FINAL_TYPE (SysprofNetdevAid, sysprof_netdev_aid, SYSPROF, NETDEV_AID, SysprofAid) + +SysprofAid *sysprof_netdev_aid_new (void); + +G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-profiler-assistant.c b/src/libsysprof-ui/sysprof-profiler-assistant.c index a8a66229..b5da881d 100644 --- a/src/libsysprof-ui/sysprof-profiler-assistant.c +++ b/src/libsysprof-ui/sysprof-profiler-assistant.c @@ -36,6 +36,7 @@ #include "sysprof-callgraph-aid.h" #include "sysprof-cpu-aid.h" #include "sysprof-memory-aid.h" +#include "sysprof-netdev-aid.h" #include "sysprof-proxy-aid.h" struct _SysprofProfilerAssistant @@ -303,11 +304,12 @@ sysprof_profiler_assistant_class_init (SysprofProfilerAssistantClass *klass) g_type_ensure (SYSPROF_TYPE_AID_ICON); g_type_ensure (SYSPROF_TYPE_BATTERY_AID); - g_type_ensure (SYSPROF_TYPE_CPU_AID); - g_type_ensure (SYSPROF_TYPE_MEMORY_AID); - g_type_ensure (SYSPROF_TYPE_PROXY_AID); - g_type_ensure (SYSPROF_TYPE_ENVIRON_EDITOR); g_type_ensure (SYSPROF_TYPE_CALLGRAPH_AID); + g_type_ensure (SYSPROF_TYPE_CPU_AID); + g_type_ensure (SYSPROF_TYPE_ENVIRON_EDITOR); + g_type_ensure (SYSPROF_TYPE_MEMORY_AID); + g_type_ensure (SYSPROF_TYPE_NETDEV_AID); + g_type_ensure (SYSPROF_TYPE_PROXY_AID); } static void diff --git a/src/libsysprof-ui/sysprof-profiler-assistant.ui b/src/libsysprof-ui/sysprof-profiler-assistant.ui index 7ce809ae..19f1341d 100644 --- a/src/libsysprof-ui/sysprof-profiler-assistant.ui +++ b/src/libsysprof-ui/sysprof-profiler-assistant.ui @@ -4,13 +4,7 @@ - - Network - preferences-system-network-symbolic - - - - + /org/gnome/Sysprof3/Profiler session diff --git a/src/libsysprof/sysprof-netdev-source.c b/src/libsysprof/sysprof-netdev-source.c index 7e637e88..68dcf7b6 100644 --- a/src/libsysprof/sysprof-netdev-source.c +++ b/src/libsysprof/sysprof-netdev-source.c @@ -303,7 +303,7 @@ sysprof_netdev_source_start (SysprofSource *source) g_assert (SYSPROF_IS_NETDEV_SOURCE (self)); - self->poll_source = g_timeout_add_seconds (1, sysprof_netdev_source_poll_cb, self); + self->poll_source = g_timeout_add (200, sysprof_netdev_source_poll_cb, self); /* Poll immediately */ sysprof_netdev_source_poll_cb (self);