[sysprof] netdev: add source for network device information



commit 00997fbd7fbe97aa7b7ade4de7f2cc1bbeb862ec
Author: Christian Hergert <chergert redhat com>
Date:   Wed Jun 26 12:38:54 2019 -0700

    netdev: add source for network device information
    
    This source parses the /proc/net/dev file to get basic statistics about
    network throughput on the system.
    
    We still need a specialized Aid and Visualizer so that we can render the
    counter data in a more useful format.

 src/libsysprof-ui/sysprof-profiler-assistant.ui |  14 +
 src/libsysprof/meson.build                      |   1 +
 src/libsysprof/sysprof-netdev-source.c          | 385 ++++++++++++++++++++++++
 src/libsysprof/sysprof-netdev-source.h          |  35 +++
 src/libsysprof/sysprof.h                        |   1 +
 5 files changed, 436 insertions(+)
---
diff --git a/src/libsysprof-ui/sysprof-profiler-assistant.ui b/src/libsysprof-ui/sysprof-profiler-assistant.ui
index 2a15f30..7ce809a 100644
--- a/src/libsysprof-ui/sysprof-profiler-assistant.ui
+++ b/src/libsysprof-ui/sysprof-profiler-assistant.ui
@@ -4,6 +4,13 @@
   <object class="SysprofCpuAid" id="cpu_aid"/>
   <object class="SysprofMemoryAid" id="memory_aid"/>
   <object class="SysprofCallgraphAid" id="callgraph_aid"/>
+  <object class="SysprofAid" id="network_aid">
+    <property name="display-name" translatable="yes">Network</property>
+    <property name="icon-name">preferences-system-network-symbolic</property>
+    <child>
+      <object class="SysprofNetdevSource"/>
+    </child>
+  </object>
   <object class="SysprofProxyAid" id="mutter_aid">
     <property name="object-path">/org/gnome/Sysprof3/Profiler</property>
     <property name="bus-type">session</property>
@@ -148,6 +155,13 @@
                             <property name="visible">true</property>
                           </object>
                         </child>
+                        <child>
+                          <object class="SysprofAidIcon">
+                            <property name="aid">network_aid</property>
+                            <property name="selected">false</property>
+                            <property name="visible">true</property>
+                          </object>
+                        </child>
                       </object>
                     </child>
                   </object>
diff --git a/src/libsysprof/meson.build b/src/libsysprof/meson.build
index 9945852..0960b67 100644
--- a/src/libsysprof/meson.build
+++ b/src/libsysprof/meson.build
@@ -15,6 +15,7 @@ libsysprof_public_sources = [
   'sysprof-kernel-symbol.c',
   'sysprof-kernel-symbol-resolver.c',
   'sysprof-local-profiler.c',
+  'sysprof-netdev-source.c',
   'sysprof-process-model.c',
   'sysprof-process-model-item.c',
   'sysprof-profile.c',
diff --git a/src/libsysprof/sysprof-netdev-source.c b/src/libsysprof/sysprof-netdev-source.c
new file mode 100644
index 0000000..7e637e8
--- /dev/null
+++ b/src/libsysprof/sysprof-netdev-source.c
@@ -0,0 +1,385 @@
+/* sysprof-netdev-source.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-netdev-source"
+
+#include "config.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <glib/gstdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "sysprof-backport-autocleanups.h"
+#include "sysprof-line-reader.h"
+#include "sysprof-netdev-source.h"
+#include "sysprof-helpers.h"
+
+struct _SysprofNetdevSource
+{
+  GObject               parent_instance;
+
+  SysprofCaptureWriter *writer;
+  GArray               *netdevs;
+
+  /* FD for /proc/net/dev contents */
+  gint                  netdev_fd;
+
+  /* GSource ID for polling */
+  guint                 poll_source;
+};
+
+typedef struct
+{
+  /* Counter IDs */
+  guint rx_bytes_id;
+  guint tx_bytes_id;
+  gchar iface[32];
+  gint64 rx_bytes;
+  gint64 rx_packets;
+  gint64 rx_errors;
+  gint64 rx_dropped;
+  gint64 rx_fifo;
+  gint64 rx_frame;
+  gint64 rx_compressed;
+  gint64 rx_multicast;
+  gint64 tx_bytes;
+  gint64 tx_packets;
+  gint64 tx_errors;
+  gint64 tx_dropped;
+  gint64 tx_fifo;
+  gint64 tx_collisions;
+  gint64 tx_carrier;
+  gint64 tx_compressed;
+} Netdev;
+
+static void source_iface_init (SysprofSourceInterface *);
+
+G_DEFINE_TYPE_WITH_CODE (SysprofNetdevSource, sysprof_netdev_source, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (SYSPROF_TYPE_SOURCE, source_iface_init))
+
+static Netdev *
+find_device_by_name (SysprofNetdevSource *self,
+                     const gchar         *name)
+{
+  g_assert (SYSPROF_IS_NETDEV_SOURCE (self));
+  g_assert (self->writer != NULL);
+  g_assert (name != NULL);
+
+  for (guint i = 0; i < self->netdevs->len; i++)
+    {
+      Netdev *netdev = &g_array_index (self->netdevs, Netdev, i);
+
+      if (strcmp (name, netdev->iface) == 0)
+        return netdev;
+    }
+
+  return NULL;
+}
+
+static Netdev *
+register_counters_by_name (SysprofNetdevSource *self,
+                           const gchar         *name)
+{
+  SysprofCaptureCounter ctr[2] = {0};
+  g_autofree gchar *rx = NULL;
+  g_autofree gchar *tx = NULL;
+  Netdev nd = {0};
+
+  g_assert (SYSPROF_IS_NETDEV_SOURCE (self));
+  g_assert (name != NULL);
+  g_assert (self->writer != NULL);
+
+  rx = g_strdup_printf ("RX Bytes (%s)", name);
+  tx = g_strdup_printf ("TX Bytes (%s)", name);
+
+  nd.rx_bytes_id = sysprof_capture_writer_request_counter (self->writer, 1);
+  nd.tx_bytes_id = sysprof_capture_writer_request_counter (self->writer, 1);
+  g_strlcpy (nd.iface, name, sizeof nd.iface);
+  g_array_append_val (self->netdevs, nd);
+
+  g_strlcpy (ctr[0].category, "Network", sizeof ctr[0].category);
+  g_strlcpy (ctr[0].name, rx, sizeof ctr[0].name);
+  g_strlcpy (ctr[0].description, name, sizeof ctr[0].description);
+  ctr[0].id = nd.rx_bytes_id;
+  ctr[0].type = SYSPROF_CAPTURE_COUNTER_INT64;
+  ctr[0].value.v64 = 0;
+
+  g_strlcpy (ctr[1].category, "Network", sizeof ctr[1].category);
+  g_strlcpy (ctr[1].name, tx, sizeof ctr[1].name);
+  g_strlcpy (ctr[1].description, name, sizeof ctr[1].description);
+  ctr[1].id = nd.tx_bytes_id;
+  ctr[1].type = SYSPROF_CAPTURE_COUNTER_INT64;
+  ctr[1].value.v64 = 0;
+
+  sysprof_capture_writer_define_counters (self->writer,
+                                          SYSPROF_CAPTURE_CURRENT_TIME,
+                                          -1,
+                                          -1,
+                                          ctr, G_N_ELEMENTS (ctr));
+
+  return &g_array_index (self->netdevs, Netdev, self->netdevs->len - 1);
+}
+
+static gboolean
+sysprof_netdev_source_get_is_ready (SysprofSource *source)
+{
+  return TRUE;
+}
+
+static void
+sysprof_netdev_source_prepare (SysprofSource *source)
+{
+  SysprofNetdevSource *self = (SysprofNetdevSource *)source;
+  g_autoptr(GArray) counters = NULL;
+  g_autoptr(GError) error = NULL;
+
+  g_assert (SYSPROF_IS_NETDEV_SOURCE (self));
+
+  self->netdev_fd = g_open ("/proc/net/dev", O_RDONLY, 0);
+
+  if (self->netdev_fd == -1)
+    {
+      int errsv = errno;
+      error = g_error_new (G_FILE_ERROR,
+                           g_file_error_from_errno (errsv),
+                           "%s",
+                           g_strerror (errsv));
+      sysprof_source_emit_failed (source, error);
+      return;
+    }
+
+  counters = g_array_new (FALSE, FALSE, sizeof (SysprofCaptureCounter));
+
+  sysprof_source_emit_ready (source);
+}
+
+static gboolean
+sysprof_netdev_source_poll_cb (gpointer data)
+{
+  g_autoptr(SysprofLineReader) reader = NULL;
+  SysprofNetdevSource *self = data;
+  g_autoptr(GArray) counters = NULL;
+  g_autoptr(GArray) values = NULL;
+  gchar buf[4096*4];
+  gssize len;
+  gsize line_len;
+  gchar *line;
+
+  g_assert (SYSPROF_IS_NETDEV_SOURCE (self));
+
+  if (self->netdev_fd == -1)
+    {
+      self->poll_source = 0;
+      return G_SOURCE_REMOVE;
+    }
+
+  /* Seek to 0 forces reload of data */
+  lseek (self->netdev_fd, 0, SEEK_SET);
+
+  len = read (self->netdev_fd, buf, sizeof buf - 1);
+
+  /* Bail for now unless we read enough data */
+  if (len > 0)
+    buf[len] = 0;
+  else
+    return G_SOURCE_CONTINUE;
+
+  counters = g_array_new (FALSE, FALSE, sizeof (guint));
+  values = g_array_new (FALSE, FALSE, sizeof (SysprofCaptureCounterValue));
+  reader = sysprof_line_reader_new (buf, len);
+
+#if 0
+Entries looks like this...
+------------------------------------------------------------------------------------------------------------------------------
+Inter-|   Receive                                                |  Transmit
+ face |bytes    packets errs drop fifo frame compressed multicast|bytes    packets errs drop fifo colls 
carrier compressed
+    lo:   10410     104    0    0    0     0          0         0    10410     104    0    0    0     0      
 0          0
+  eth0:1069675772 4197670    0    0    0     0          0         0 3221945712 3290571    0    0    0     0  
     0          0
+------------------------------------------------------------------------------------------------------------------------------
+#endif
+
+  /* Skip first two lines */
+  for (guint i = 0; i < 2; i++)
+    {
+      if (!(line = (gchar *)sysprof_line_reader_next (reader, &line_len)))
+        return G_SOURCE_CONTINUE;
+    }
+
+  while ((line = (gchar *)sysprof_line_reader_next (reader, &line_len)))
+    {
+      Netdev *nd;
+      gchar *name;
+      gchar *ptr = line;
+
+      line[line_len] = 0;
+
+      for (; *ptr && g_ascii_isspace (*ptr); ptr++) { /* Do Nothing */ }
+      name = ptr;
+      for (; *ptr && *ptr != ':'; ptr++) { /* Do Nothing */ }
+      *ptr = 0;
+
+      if (!(nd = find_device_by_name (self, name)))
+        nd = register_counters_by_name (self, name);
+
+      ptr++;
+
+      sscanf (ptr,
+              "%"G_GINT64_FORMAT
+              " %"G_GINT64_FORMAT
+              " %"G_GINT64_FORMAT
+              " %"G_GINT64_FORMAT
+              " %"G_GINT64_FORMAT
+              " %"G_GINT64_FORMAT
+              " %"G_GINT64_FORMAT
+              " %"G_GINT64_FORMAT
+              " %"G_GINT64_FORMAT
+              " %"G_GINT64_FORMAT
+              " %"G_GINT64_FORMAT
+              " %"G_GINT64_FORMAT
+              " %"G_GINT64_FORMAT
+              " %"G_GINT64_FORMAT
+              " %"G_GINT64_FORMAT
+              " %"G_GINT64_FORMAT,
+              &nd->rx_bytes,
+              &nd->rx_packets,
+              &nd->rx_errors,
+              &nd->rx_dropped,
+              &nd->rx_fifo,
+              &nd->rx_frame,
+              &nd->rx_compressed,
+              &nd->rx_multicast,
+              &nd->tx_bytes,
+              &nd->tx_packets,
+              &nd->tx_errors,
+              &nd->tx_dropped,
+              &nd->tx_fifo,
+              &nd->tx_collisions,
+              &nd->tx_carrier,
+              &nd->tx_compressed);
+
+      g_array_append_val (counters, nd->rx_bytes_id);
+      g_array_append_val (values, nd->rx_bytes);
+
+      g_array_append_val (counters, nd->tx_bytes_id);
+      g_array_append_val (values, nd->tx_bytes);
+    }
+
+  if (counters->len)
+    sysprof_capture_writer_set_counters (self->writer,
+                                         SYSPROF_CAPTURE_CURRENT_TIME,
+                                         -1,
+                                         -1,
+                                         (const guint *)(gpointer)counters->data,
+                                         (const SysprofCaptureCounterValue *)(gpointer)values->data,
+                                         counters->len);
+
+  return G_SOURCE_CONTINUE;
+}
+
+static void
+sysprof_netdev_source_start (SysprofSource *source)
+{
+  SysprofNetdevSource *self = (SysprofNetdevSource *)source;
+
+  g_assert (SYSPROF_IS_NETDEV_SOURCE (self));
+
+  self->poll_source = g_timeout_add_seconds (1, sysprof_netdev_source_poll_cb, self);
+
+  /* Poll immediately */
+  sysprof_netdev_source_poll_cb (self);
+}
+
+static void
+sysprof_netdev_source_stop (SysprofSource *source)
+{
+  SysprofNetdevSource *self = (SysprofNetdevSource *)source;
+
+  g_assert (SYSPROF_IS_NETDEV_SOURCE (self));
+
+  /* Poll one last time */
+  sysprof_netdev_source_poll_cb (self);
+
+  g_clear_handle_id (&self->poll_source, g_source_remove);
+
+  sysprof_source_emit_finished (source);
+}
+
+static void
+sysprof_netdev_source_set_writer (SysprofSource        *source,
+                                  SysprofCaptureWriter *writer)
+{
+  SysprofNetdevSource *self = (SysprofNetdevSource *)source;
+
+  g_assert (SYSPROF_IS_NETDEV_SOURCE (self));
+  g_assert (writer != NULL);
+
+  g_clear_pointer (&self->writer, sysprof_capture_writer_unref);
+  self->writer = sysprof_capture_writer_ref (writer);
+}
+
+static void
+source_iface_init (SysprofSourceInterface *iface)
+{
+  iface->get_is_ready = sysprof_netdev_source_get_is_ready;
+  iface->prepare = sysprof_netdev_source_prepare;
+  iface->set_writer = sysprof_netdev_source_set_writer;
+  iface->start = sysprof_netdev_source_start;
+  iface->stop = sysprof_netdev_source_stop;
+}
+
+static void
+sysprof_netdev_source_finalize (GObject *object)
+{
+  SysprofNetdevSource *self = (SysprofNetdevSource *)object;
+
+  g_clear_pointer (&self->netdevs, g_array_unref);
+  g_clear_pointer (&self->writer, sysprof_capture_writer_unref);
+
+  if (self->netdev_fd != -1)
+    {
+      close (self->netdev_fd);
+      self->netdev_fd = -1;
+    }
+
+  G_OBJECT_CLASS (sysprof_netdev_source_parent_class)->finalize (object);
+}
+
+static void
+sysprof_netdev_source_class_init (SysprofNetdevSourceClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = sysprof_netdev_source_finalize;
+}
+
+static void
+sysprof_netdev_source_init (SysprofNetdevSource *self)
+{
+  self->netdevs = g_array_new (FALSE, FALSE, sizeof (Netdev));
+}
+
+SysprofSource *
+sysprof_netdev_source_new (void)
+{
+  return g_object_new (SYSPROF_TYPE_NETDEV_SOURCE, NULL);
+}
diff --git a/src/libsysprof/sysprof-netdev-source.h b/src/libsysprof/sysprof-netdev-source.h
new file mode 100644
index 0000000..4fcc448
--- /dev/null
+++ b/src/libsysprof/sysprof-netdev-source.h
@@ -0,0 +1,35 @@
+/* sysprof-netdev-source.h
+ *
+ * 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
+ */
+
+#pragma once
+
+#include "sysprof-source.h"
+
+G_BEGIN_DECLS
+
+#define SYSPROF_TYPE_NETDEV_SOURCE (sysprof_netdev_source_get_type())
+
+SYSPROF_AVAILABLE_IN_ALL
+G_DECLARE_FINAL_TYPE (SysprofNetdevSource, sysprof_netdev_source, SYSPROF, NETDEV_SOURCE, GObject)
+
+SYSPROF_AVAILABLE_IN_ALL
+SysprofSource *sysprof_netdev_source_new (void);
+
+G_END_DECLS
diff --git a/src/libsysprof/sysprof.h b/src/libsysprof/sysprof.h
index 1a00bc6..f5f3507 100644
--- a/src/libsysprof/sysprof.h
+++ b/src/libsysprof/sysprof.h
@@ -35,6 +35,7 @@ G_BEGIN_DECLS
 # include "sysprof-kernel-symbol-resolver.h"
 # include "sysprof-kernel-symbol.h"
 # include "sysprof-local-profiler.h"
+# include "sysprof-netdev-source.h"
 # include "sysprof-process-model-item.h"
 # include "sysprof-process-model.h"
 # include "sysprof-profile.h"


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]