[Xfce4-commits] [panel-plugins/xfce4-hardware-monitor-plugin] 03/18: Initial implementation of DiskStatsMonitor

noreply at xfce.org noreply at xfce.org
Tue Jun 28 21:33:25 CEST 2016


This is an automated email from the git hooks/post-receive script.

omegaphil pushed a commit to branch omegaphil/graph-disk-io
in repository panel-plugins/xfce4-hardware-monitor-plugin.

commit 2dbb964b5b00b11661321f852fd2198abe785bb1
Author: OmegaPhil <OmegaPhil at startmail.com>
Date:   Fri Jun 10 17:57:19 2016 +0100

    Initial implementation of DiskStatsMonitor
---
 configure.ac                  |   2 +-
 src/choose-monitor-window.cpp | 153 ++++++++++++++--
 src/choose-monitor-window.hpp |  28 ++-
 src/monitor-impls.cpp         | 414 +++++++++++++++++++++++++++++++++++++++++-
 src/monitor-impls.hpp         |  60 ++++++
 src/ui.glade                  |   2 +
 6 files changed, 630 insertions(+), 29 deletions(-)

diff --git a/configure.ac b/configure.ac
index 4a6aa45..7a79eba 100644
--- a/configure.ac
+++ b/configure.ac
@@ -58,7 +58,7 @@ XDT_CHECK_PACKAGE([LIBXFCE4PANEL], [libxfce4panel-1.0], [4.8.0])
 XDT_CHECK_PACKAGE([LIBXFCE4UI], [libxfce4ui-1], [4.8.0])
 
 dnl Checks for libraries
-PKG_CHECK_MODULES([DEPS], [gtkmm-2.4 >= 2.6.0 \
+PKG_CHECK_MODULES([DEPS], [gtkmm-2.4 >= 2.24.0 \
                         libgnomecanvasmm-2.6 >= 2.6.0 \
                         libgtop-2.0 >= 2.6.0])
 
diff --git a/src/choose-monitor-window.cpp b/src/choose-monitor-window.cpp
index 674564c..2f44fdf 100644
--- a/src/choose-monitor-window.cpp
+++ b/src/choose-monitor-window.cpp
@@ -80,6 +80,8 @@ ChooseMonitorWindow::ChooseMonitorWindow(XfcePanelPlugin* panel_applet_local,
   ui->get_widget("mount_dir_entry", mount_dir_entry);
   ui->get_widget("show_free_checkbutton", show_free_checkbutton);
   ui->get_widget("disk_usage_tag_entry", disk_usage_tag);
+  ui->get_widget("disk_stats_device_combobox", disk_stats_device_combobox);
+  ui->get_widget("disk_stats_stat_combobox", disk_stats_stat_combobox);
   ui->get_widget("disk_stats_tag_entry", disk_stats_tag);
   ui->get_widget("memory_tag_entry", memory_usage_tag);
   ui->get_widget("swap_tag_entry", swap_usage_tag);
@@ -158,6 +160,35 @@ ChooseMonitorWindow::ChooseMonitorWindow(XfcePanelPlugin* panel_applet_local,
   device_notebook->get_nth_page(3)->hide();
 #endif
 
+  // Setup disk statistics device name combobox
+  static DiskStatsDeviceNameCols dsdnc;
+  disk_stats_device_name_store = Gtk::ListStore::create(dsdnc);
+  disk_stats_device_combobox->set_model(disk_stats_device_name_store);
+  disk_stats_device_combobox->pack_start(dsdnc.device_name);
+
+  std::vector<Glib::ustring> device_names = DiskStatsMonitor::current_device_names();
+  for (std::vector<Glib::ustring>::iterator it = device_names.begin();
+       it != device_names.end(); ++it)
+  {
+      store_iter iter = disk_stats_device_name_store->append();
+      (*iter)[dsdnc.device_name] = *it;
+  }
+
+  // Setup disk statistics stat combobox
+  static DiskStatsStatCols dssc;
+  disk_stats_stat_store = Gtk::ListStore::create(dssc);
+  disk_stats_stat_combobox->set_model(disk_stats_stat_store);
+  disk_stats_stat_combobox->pack_start(dssc.stat);
+
+  for (int i = 0; i < DiskStatsMonitor::NUM_STATS; ++i)
+  {
+      DiskStatsMonitor::Stat stat =
+          static_cast<DiskStatsMonitor::Stat>(i);
+      store_iter iter = disk_stats_stat_store->append();
+      (*iter)[dssc.stat] = DiskStatsMonitor::
+          stat_to_string(stat, false);
+  }
+
   // Setup network interface type combobox
   static NetworkInterfaceTypeCols nitc;
   network_interface_type_store = Gtk::ListStore::create(nitc);
@@ -303,6 +334,12 @@ Monitor *ChooseMonitorWindow::run(const Glib::ustring &mon_dir)
         disk_usage_radiobutton->set_active();
         disk_usage_tag->set_text(tag);
       }
+      else if (type == "disk_statistics")
+      {
+        device_notebook->set_current_page(1);
+        disk_stats_radiobutton->set_active();
+        disk_stats_tag->set_text(tag);
+      }
       else if (type == "swap_usage")
       {
         device_notebook->set_current_page(1);
@@ -324,7 +361,6 @@ Monitor *ChooseMonitorWindow::run(const Glib::ustring &mon_dir)
       else
       {
         device_notebook->set_current_page(0);
-        // FIXME: use schema?
         cpu_usage_radiobutton->set_active();
         cpu_tag->set_text(tag);
       }
@@ -356,6 +392,44 @@ Monitor *ChooseMonitorWindow::run(const Glib::ustring &mon_dir)
         show_free_checkbutton->set_active(show_free);
       }
 
+      // Fill in disk stats info
+      if (xfce_rc_has_entry(settings_ro, "disk_stats_device"))
+      {
+        Glib::ustring device_name = xfce_rc_read_entry(settings_ro,
+          "disk_stats_device", "");
+
+        // Locating device in the model
+        static DiskStatsDeviceNameCols dsdnc;
+        Gtk::TreeNodeChildren children = disk_stats_device_name_store->children();
+        bool device_found = false;
+        for (Gtk::TreeIter it = children.begin(); it < children.end(); ++it)
+        {
+          if (it->get_value(dsdnc.device_name) == device_name)
+          {
+            device_found = true;
+            disk_stats_device_combobox->set_active(it);
+            break;
+          }
+        }
+
+        // Add as user-defined text if the device isn't currently available
+        if (!device_found)
+          disk_stats_device_combobox->get_entry()->set_text(device_name);
+
+        // Selecting the correct statistic
+        int stat = xfce_rc_read_int_entry(settings_ro, "disk_stats_stat", 0);
+        if (stat < 0 || stat >= DiskStatsMonitor::NUM_STATS)
+          stat = 0;
+        disk_stats_stat_combobox->set_active(stat);
+      }
+
+      if (xfce_rc_has_entry(settings_ro, "show_free"))
+      {
+        bool show_free  = xfce_rc_read_bool_entry(settings_ro,
+          "show_free", false);
+        show_free_checkbutton->set_active(show_free);
+      }
+
       // Fill in network load info
       if (xfce_rc_has_entry(settings_ro, "interface_type"))
       {
@@ -425,7 +499,6 @@ Monitor *ChooseMonitorWindow::run(const Glib::ustring &mon_dir)
     {
       // No monitor present so an addition - defaults
       device_notebook->set_current_page(0);
-      // FIXME: use schema?
       cpu_usage_radiobutton->set_active();
     }
 
@@ -464,9 +537,9 @@ Monitor *ChooseMonitorWindow::run(const Glib::ustring &mon_dir)
   }
 
   if (cpu_usage_radiobutton->get_active())
-    cpu_usage_radiobutton->toggled(); // send a signal
+    cpu_usage_radiobutton->toggled();  // Send a signal
 
-  // then ask the user
+  // Then ask the user
   int response;
   
   do {
@@ -476,42 +549,86 @@ Monitor *ChooseMonitorWindow::run(const Glib::ustring &mon_dir)
       Monitor *mon = 0;
 
       if (cpu_usage_radiobutton->get_active())
+      {
         if (one_cpu_radiobutton->get_active())
           mon = new CpuUsageMonitor(int(cpu_no_spinbutton->get_value()) - 1,
                                     cpu_tag->get_text());
         else
           mon = new CpuUsageMonitor(cpu_tag->get_text());
+      }
       else if (memory_usage_radiobutton->get_active())
         mon = new MemoryUsageMonitor(memory_usage_tag->get_text());
       else if (swap_usage_radiobutton->get_active())
         mon = new SwapUsageMonitor(swap_usage_tag->get_text());
       else if (load_average_radiobutton->get_active())
         mon = new LoadAverageMonitor(load_average_tag->get_text());
-      else if (disk_usage_radiobutton->get_active()) {
+      else if (disk_usage_radiobutton->get_active())
+      {
         Glib::ustring mount_dir = mount_dir_entry->get_text();
         bool show_free = show_free_checkbutton->get_active();
-        // FIXME: check that mount_dir is valid
+        // TODO: check that mount_dir is valid
         mon = new DiskUsageMonitor(mount_dir, show_free,
                                    disk_usage_tag->get_text());
       }
+      else if (disk_stats_radiobutton->get_active())
+      {
+        Glib::ustring device_name =
+            disk_stats_device_combobox->get_entry_text();
+        DiskStatsMonitor::Stat stat =
+            static_cast<DiskStatsMonitor::Stat>(
+              disk_stats_stat_combobox->get_active_row_number());
+
+        /* Originally this validation code was in the changed signal handler but
+         * that fired on every keystroke, then in the focus_out handler but
+         * subsequent grab focus calls didn't work in it...
+         * Making sure the device exists (since the user can put anything in
+         * here) */
+        if (!Glib::file_test("/dev/" + device_name,
+                             Glib::FileTest::FILE_TEST_EXISTS))
+        {
+          /* Making sure the user is OK with specifying a non-existent device
+           * (i.e. it may appear later) */
+          Glib::ustring msg = Glib::ustring::
+              compose(_("Specified device '%1' does not currently exist - do you"
+                           " still want to proceed?"), device_name);
+
+          /* See helpers.hpp - tried to host a generic warning dialog
+           * implementation there but got endless include bullshit */
+          Gtk::MessageDialog d(msg, false, Gtk::MESSAGE_WARNING,
+                               Gtk::BUTTONS_YES_NO);
+          d.set_modal();
+          d.set_title(_("Disk Stats Monitor"));
+          d.set_icon(window->get_icon());
+          if (d.run() != Gtk::RESPONSE_YES)
+          {
+            disk_stats_device_combobox->get_entry()->grab_focus();
+            response = Gtk::RESPONSE_HELP;
+            continue;
+          }
+        }
+
+        mon = new DiskStatsMonitor(device_name, stat,
+                                   disk_stats_tag->get_text());
+      }
       else if (network_load_radiobutton->get_active())
       {
         int selected_type = network_type_combobox->get_active_row_number();
         NetworkLoadMonitor::InterfaceType interface_type = static_cast<NetworkLoadMonitor::InterfaceType>(selected_type);
 
         NetworkLoadMonitor::Direction dir;
-        switch (network_direction_combobox->get_active_row_number()) {
-        case NetworkLoadMonitor::incoming_data:
-          dir = NetworkLoadMonitor::incoming_data;
-          break;
-
-        case NetworkLoadMonitor::outgoing_data:
-          dir = NetworkLoadMonitor::outgoing_data;
-          break;
-
-        default:
-          dir = NetworkLoadMonitor::all_data;
-          break;
+        switch (network_direction_combobox->get_active_row_number())
+        {
+          case NetworkLoadMonitor::incoming_data:
+            dir = NetworkLoadMonitor::incoming_data;
+            break;
+
+          case NetworkLoadMonitor::outgoing_data:
+            dir = NetworkLoadMonitor::outgoing_data;
+            break;
+
+          default:
+            dir = NetworkLoadMonitor::all_data;
+            break;
         }
 
         mon = new NetworkLoadMonitor(interface_type, dir,
diff --git a/src/choose-monitor-window.hpp b/src/choose-monitor-window.hpp
index c472776..05396c0 100644
--- a/src/choose-monitor-window.hpp
+++ b/src/choose-monitor-window.hpp
@@ -82,6 +82,8 @@ private:
              *swap_usage_tag;
   Gtk::CheckButton *show_free_checkbutton;
 
+  Gtk::ComboBox *disk_stats_device_combobox, *disk_stats_stat_combobox;
+
   Gtk::Box *network_load_options;
   Gtk::ComboBox *network_type_combobox, *network_direction_combobox;
   Gtk::TreeView *network_interfaces_treeview;
@@ -95,6 +97,30 @@ private:
 
   XfcePanelPlugin* panel_applet;
 
+  // For disk statistics device name combobox
+  class DiskStatsDeviceNameCols: public Gtk::TreeModel::ColumnRecord
+  {
+  public:
+    Gtk::TreeModelColumn<Glib::ustring> device_name;
+
+    DiskStatsDeviceNameCols() { add(device_name); }
+  };
+
+  Glib::RefPtr<Gtk::ListStore> disk_stats_device_name_store;
+
+  typedef Gtk::ListStore::iterator store_iter;
+
+  // For disk statistics stat combobox
+  class DiskStatsStatCols: public Gtk::TreeModel::ColumnRecord
+  {
+  public:
+    Gtk::TreeModelColumn<Glib::ustring> stat;
+
+    DiskStatsStatCols() { add(stat); }
+  };
+
+  Glib::RefPtr<Gtk::ListStore> disk_stats_stat_store;
+
   // For network interface type combobox (basic listing of available types)
   class NetworkInterfaceTypeCols: public Gtk::TreeModel::ColumnRecord
   {
@@ -106,8 +132,6 @@ private:
 
   Glib::RefPtr<Gtk::ListStore> network_interface_type_store;
 
-  typedef Gtk::ListStore::iterator store_iter;
-
   // For network direction combobox
   class NetworkDirectionCols: public Gtk::TreeModel::ColumnRecord
   {
diff --git a/src/monitor-impls.cpp b/src/monitor-impls.cpp
index c6d174d..0e9549f 100644
--- a/src/monitor-impls.cpp
+++ b/src/monitor-impls.cpp
@@ -19,16 +19,14 @@
  * USA.
  */
 
-#include <string>
+#include <algorithm>
+#include <iomanip>  // Needed for Precision helper
 #include <iostream>
-#include <iomanip>
-#include <ostream>
-#include <sys/time.h>       // for high-precision timing for the network load
+#include <string>
 #include <vector>
-#include <algorithm>
-#include <cstdio>
-#include <cassert>
-#include <cstdlib>
+
+#include <glibmm/fileutils.h>
+#include <glibmm/regex.h>
 
 #include <glibtop.h>
 #include <glibtop/cpu.h>
@@ -39,6 +37,8 @@
 #include <glibtop/netload.h>
 #include <glibtop/netlist.h>
 
+#include <sys/time.h>       // for high-precision timing for the network load
+
 #include "monitor-impls.hpp"
 #include "ucompose.hpp"
 #include "i18n.hpp"
@@ -109,6 +109,19 @@ load_monitors(XfceRc *settings_ro, XfcePanelPlugin *panel_plugin)
         // Creating disk usage monitor
         monitors.push_back(new DiskUsageMonitor(mount_dir, show_free, tag));
       }
+      else if (type == "disk_statistics")
+      {
+        Glib::ustring device_name = xfce_rc_read_entry(settings_ro,
+          "disk_stats_device", "");
+
+        DiskStatsMonitor::Stat stat =
+            static_cast<DiskStatsMonitor::Stat>(xfce_rc_read_int_entry(
+                                                settings_ro,                                                                                                                                       "disk_stats_stat",
+                                               DiskStatsMonitor::num_reads_completed));
+
+        // Creating disk statistics monitor
+        monitors.push_back(new DiskStatsMonitor(device_name, stat, tag));
+      }
       else if (type == "network_load")
       {
         NetworkLoadMonitor::InterfaceType inter_type(NetworkLoadMonitor::ethernet_first);
@@ -709,6 +722,391 @@ void DiskUsageMonitor::save(XfceRc *settings_w)
   xfce_rc_write_entry(settings_w, "tag", tag.c_str());
 }
 
+//
+// class DiskStatsMonitor
+//
+// Static initialisation
+const Glib::ustring& DiskStatsMonitor::diskstats_path = "/proc/diskstats";
+
+// No stats allow for negative values, so using that to detect no previous value
+DiskStatsMonitor::DiskStatsMonitor(const Glib::ustring &device_name,
+                                   const Stat &stat_to_monitor,
+                                   const Glib::ustring &tag_string)
+  : Monitor(tag_string), device_name(device_name),
+    stat_to_monitor(stat_to_monitor), previous_value(-1)
+{
+}
+
+double DiskStatsMonitor::do_measure()
+{
+  // Making sure stats file is available
+  if (!stats_available())
+  {
+    std::cerr << Glib::ustring::compose(_("The file '%1' is not available - "
+                                          "unable to obtain %2 for device '%3'!"
+                                          "\n"), diskstats_path,
+                                         stat_to_string(stat_to_monitor, false),
+                                         device_name);
+    return 0;
+  }
+
+  /* Returning 0 if device is not available - this is not an error since the
+   * device may be hotpluggable */
+  std::map<Glib::ustring, std::vector<unsigned long int>> disk_stats =
+      parse_disk_stats();
+  std::map<Glib::ustring, std::vector<unsigned long int>>::iterator it =
+      disk_stats.find(device_name);
+  if (it == disk_stats.end())
+  {
+    // Debug code
+    /*std::cerr << Glib::ustring::compose(_("Unable to find device '%1' to obtain "
+                                          "%2 from!\n"), device_name,
+                                        stat_to_string(stat_to_monitor, false));*/
+
+    return 0;
+  }
+
+  // Debug code
+  /*std::cerr << Glib::ustring::compose("Device '%1' stat %2: %3\n", device_name,
+                                      stat_to_string(stat_to_monitor, false),
+                                      it->second[stat_to_monitor]);*/
+
+  double val;
+  if (convert_to_rate())
+  {
+    /* Stats that need to be diffed to make a rate of change
+     * Dealing with the first value to be processed */
+    if (previous_value == -1)
+      previous_value = it->second[stat_to_monitor];
+
+    // Returning desired stat
+    val = it->second[stat_to_monitor] - previous_value;
+    previous_value = it->second[stat_to_monitor];
+  }
+  else
+  {
+    /* Stats that don't need to be returned as a rate of change (per second
+     * currently) */
+    val = it->second[stat_to_monitor];
+  }
+
+  /* Note - max_value is no longer used to determine the graph max for
+   * Curves - the actual maxima stored in the ValueHistories are used */
+  if (val != 0)     // Reduce scale gradually
+    max_value = guint64(max_value * max_decay);
+
+  if (val > max_value)
+    max_value = guint64(val * 1.05);
+
+  return val;
+}
+
+double DiskStatsMonitor::max()
+{
+  return max_value;
+}
+
+bool DiskStatsMonitor::fixed_max()
+{
+  return false;
+}
+
+Glib::ustring DiskStatsMonitor::format_value(double val, bool compact)
+{
+  // Currently measurement is every second
+  Glib::ustring unit = (convert_to_rate() && !compact) ? "/s" : "";
+  return Glib::ustring::compose("%1%2", val, unit);
+}
+
+Glib::ustring DiskStatsMonitor::get_name()
+{
+  return device_name + " - " + stat_to_string(stat_to_monitor, false);
+}
+
+Glib::ustring DiskStatsMonitor::get_short_name()
+{
+  return device_name + "-" + stat_to_string(stat_to_monitor, true);
+}
+
+int DiskStatsMonitor::update_interval()
+{
+  return 1000;
+}
+
+void DiskStatsMonitor::save(XfceRc *settings_w)
+{
+  // Fetching assigned settings group
+  Glib::ustring dir = get_settings_dir();
+
+  // Saving settings
+  xfce_rc_set_group(settings_w, dir.c_str());
+  xfce_rc_write_entry(settings_w, "type", "disk_statistics");
+  xfce_rc_write_entry(settings_w, "disk_stats_device", device_name.c_str());
+  xfce_rc_write_int_entry(settings_w, "disk_stats_stat", int(stat_to_monitor));
+  xfce_rc_write_int_entry(settings_w, "max", int(max_value));
+  xfce_rc_write_entry(settings_w, "tag", tag.c_str());
+}
+
+void DiskStatsMonitor::load(XfceRc *settings_ro)
+{
+  /*
+   * // TODO: This seems to be completely unnecessary - loading/configuration is already done in load_monitors, looks like that should be moved into individual monitor ::load functions?
+  // Fetching assigned settings group
+  Glib::ustring dir = get_settings_dir();
+
+  // Loading settings
+  xfce_rc_set_group(settings_ro, dir.c_str());
+  Glib::ustring type = xfce_rc_read_entry(settings_ro, "type", "");
+  device_name = xfce_rc_read_entry(settings_ro, "disk_stats_device", "");
+  int stat = xfce_rc_read_int_entry(settings_ro, "interface_type",
+                                              int(num_reads_completed));
+
+  // Validating input - an enum does not enforce a range!!
+  if (stat < num_reads_completed || stat >= NUM_STATS)
+  {
+    std::cerr << "DiskStatsMonitor::load has read configuration specifying an "
+                 "invalid statistic: " << stat << "!\n";
+    stat = num_reads_completed;
+  }
+  else
+    inter_type = static_cast<InterfaceType>(inter_type_int);
+
+  Direction inter_direction;
+  if (inter_direction_int < all_data || inter_direction_int >= NUM_DIRECTIONS)
+  {
+    std::cerr << "NetworkLoadMonitor::load has read configuration specifying an "
+                 "invalid direction: " << inter_direction_int << "!\n";
+    inter_direction = all_data;
+  }
+  else
+    inter_direction = static_cast<Direction>(inter_direction_int);
+
+  // Making sure the monitor type is correct to load further configuration??
+  if (type == "network_load" && inter_type == interface_type
+      && inter_direction == direction)
+      max_value = xfce_rc_read_int_entry(settings_ro, "max", 0);
+  */
+}
+
+bool DiskStatsMonitor::stats_available()
+{
+  // Make sure file exists
+  return Glib::file_test(diskstats_path, Glib::FileTest::FILE_TEST_EXISTS);
+
+  /* The contents of the file will be validated as it is processed, so not
+   * duplicating this here */
+}
+
+std::map<Glib::ustring, std::vector<unsigned long int>>
+DiskStatsMonitor::parse_disk_stats()
+{
+  Glib::ustring device_stats;
+
+  // Fetching contents of diskstats file
+  try
+  {
+    device_stats = Glib::file_get_contents("/proc/diskstats");
+  }
+  catch (Glib::FileError const &e)
+  {
+    std::cerr << Glib::ustring::compose(_("Unable to parse disk stats from '%1' "
+                                          "due to error '%2'\n"),
+                                        "/proc/diskstats", e.what());
+    return std::map<Glib::ustring, std::vector<unsigned long int>>();
+  }
+
+  /* Preparing regex to use in splitting out stats
+   * Example line:
+   *    8      16 sdb 16710337 4656786 7458292624 49395796 15866670 4083490 5442473656 53095516 0 24513196 102484768 */
+  Glib::RefPtr<Glib::Regex> split_stats_regex = Glib::Regex::create(
+        "^\\s+(\\d+)\\s+(\\d+)\\s([\\w-]+)\\s(\\d+)\\s(\\d+)\\s(\\d+)\\s(\\d+)\\s"
+        "(\\d+)\\s(\\d+)\\s(\\d+)\\s(\\d+)\\s(\\d+)\\s(\\d+)\\s(\\d+)$",
+        Glib::REGEX_OPTIMIZE);
+
+  // Splitting out stats into devices
+  std::map<Glib::ustring, std::vector<unsigned long int>> parsed_stats;
+  std::stringstream device_stats_stream(device_stats);
+  Glib::ustring device_name, single_dev_stats;
+  Glib::MatchInfo match_info;
+  for (std::string single_device_stats;
+       std::getline(device_stats_stream, single_device_stats);)
+  {
+    // Glib::Regex can't cope with std::string so this extra step is needed...
+    single_dev_stats = single_device_stats;
+
+    // Splitting out device stats into individual fields
+    if (!split_stats_regex->match(single_dev_stats, match_info))
+    {
+      // Unable to parse the device stats - warning user and moving on
+      std::cerr << Glib::ustring::compose("Unable to parse device stats line "
+                                            "from '%1' - regex match failure:\n"
+                                            "\n%2\n", "/proc/diskstats",
+                                            single_device_stats);
+      continue;
+    }
+
+    /* Device stats start from the 4th field onwards, also messing about to
+     * convert to correct data type
+     * Source data is stored in kernel source include/linux/genhd.h dis_stats
+     * struct, printing out to file is done in block/genhd.c:diskstats_show,
+     * some are actually unsigned ints */
+    std::vector<unsigned long int> device_parsed_stats;
+    device_name = match_info.fetch(3);
+
+    /* Debug code
+    std::cout << "Parsing device '" << device_name << "' stats...\nMatch count:"
+              << match_info.get_match_count() << "\n";
+    std::cout << "1: '" << match_info.fetch(1) << "', 2: '" << match_info.fetch(2) << "'\n";
+    std::cout << "single_device_stats: '" << single_device_stats << "'\n";
+    */
+
+    for (int i = 4; i<match_info.get_match_count(); ++i)
+    {
+      unsigned long int stat = 0;
+
+      /* Stringstreams are not trivially reusable! Hence creating a new one
+       * each time... */
+      std::stringstream convert;
+      convert.str(match_info.fetch(i));
+      if (!(convert >> stat))
+      {
+          std::cerr << Glib::ustring::compose("Unable to convert device stat %1 "
+                                              "to int from '%2' - "
+                                              "defaulting to 0\n", i,
+                                              convert.str());
+      }
+      device_parsed_stats.push_back(stat);
+
+      // Debug code
+      //std::cout << "Stat number " << i << " value: " << stat << "\n";
+    }
+    parsed_stats[device_name] = device_parsed_stats;
+  }
+
+  return parsed_stats;
+}
+
+std::vector<Glib::ustring> DiskStatsMonitor::current_device_names()
+{
+  // Fetching current disk stats
+  std::map<Glib::ustring, std::vector<unsigned long int>> parsed_stats =
+      parse_disk_stats();
+
+  // Generating sorted list of available devices
+  std::vector<Glib::ustring> devices_list;
+  for (std::map<Glib::ustring, std::vector<unsigned long int>>::iterator it
+       = parsed_stats.begin(); it != parsed_stats.end(); ++it)
+  {
+    devices_list.push_back(it->first);
+  }
+  std::sort(devices_list.begin(), devices_list.end());
+
+  return devices_list;
+}
+
+Glib::ustring DiskStatsMonitor::stat_to_string(const DiskStatsMonitor::Stat &stat,
+                                               const bool short_ver)
+{
+  Glib::ustring stat_str;
+
+  switch(stat)
+  {
+    case num_reads_completed:
+      if (short_ver)
+        stat_str = _("Num rd compl");
+      else
+        stat_str = _("Number of reads completed");
+      break;
+
+    case num_reads_merged:
+      if (short_ver)
+        stat_str = _("Num rd merg");
+      else
+        stat_str = _("Number of reads merged");
+      break;
+
+    case num_sectors_read:
+      if (short_ver)
+        stat_str = _("Num sect rd");
+      else
+        stat_str = _("Number of sectors read");
+      break;
+
+    case num_ms_reading:
+      if (short_ver)
+        stat_str = _("Num ms rd");
+      else
+        stat_str = _("Number of milliseconds spent reading");
+      break;
+
+    case num_writes_completed:
+      if (short_ver)
+        stat_str = _("Num wr compl");
+      else
+        stat_str = _("Number of writes completed");
+      break;
+
+    case num_writes_merged:
+      if (short_ver)
+        stat_str = _("Num wr merg");
+      else
+        stat_str = _("Number of writes merged");
+      break;
+
+    case num_sectors_written:
+      if (short_ver)
+        stat_str = _("Num sect wr");
+      else
+        stat_str = _("Number of sectors written");
+      break;
+
+    case num_ms_writing:
+      if (short_ver)
+        stat_str = _("Num ms wrt");
+      else
+        stat_str = _("Number of milliseconds spent writing");
+      break;
+
+    case num_ios_in_progress:
+      if (short_ver)
+        stat_str = _("Num I/Os");
+      else
+        stat_str = _("Number of I/Os in progress");
+      break;
+
+    case num_ms_doing_ios:
+      if (short_ver)
+        stat_str = _("Num ms I/Os");
+      else
+        stat_str = _("Number of milliseconds spent doing I/Os");
+      break;
+
+    case num_ms_doing_ios_weighted:
+      if (short_ver)
+        stat_str = _("Num ms I/Os wt");
+      else
+        stat_str = _("Weighted number of milliseconds spent doing I/Os");
+      break;
+  }
+
+  return stat_str;
+}
+
+bool DiskStatsMonitor::convert_to_rate()
+{
+  switch (stat_to_monitor)
+  {
+    /* Stats that don't need to be returned as a rate of change (per second
+     * currently) */
+    case num_ios_in_progress:
+      return false;
+
+    // Stats that need to be diffed to make a rate of change
+    default:
+      return true;
+  }
+
+}
 
 //
 // class NetworkLoadMonitor
diff --git a/src/monitor-impls.hpp b/src/monitor-impls.hpp
index d7d0ab0..3365a7e 100644
--- a/src/monitor-impls.hpp
+++ b/src/monitor-impls.hpp
@@ -24,6 +24,7 @@
 
 #include <config.h>
 
+#include <map>
 #include <string>
 #include <vector>
 
@@ -158,6 +159,65 @@ private:
   bool show_free;
 };
 
+class DiskStatsMonitor: public Monitor
+{
+public:
+
+  /* Based on kernel Documentation/iostats.txt - available since kernel v2.5.69
+   * If you change this, remember to update DiskStatsMonitor::stat_to_string */
+  enum Stat {
+    num_reads_completed,        // # of reads completed
+    num_reads_merged,           // # of reads merged
+    num_sectors_read,           // # of sectors read
+    num_ms_reading,             // # of milliseconds spent reading
+    num_writes_completed,       // # of writes completed
+    num_writes_merged,          // # of writes merged
+    num_sectors_written,        // # of sectors written
+    num_ms_writing,             // # of milliseconds spent writing
+    num_ios_in_progress,        // # of I/Os currently in progress
+    num_ms_doing_ios,           // # of milliseconds spent doing I/Os
+    num_ms_doing_ios_weighted,  // weighted # of milliseconds spent doing I/Os
+    NUM_STATS
+  };
+
+  DiskStatsMonitor(const Glib::ustring &device_name, const Stat &stat_to_monitor,
+                   const Glib::ustring &tag_string);
+
+  virtual double max();
+  virtual bool fixed_max();
+  virtual Glib::ustring format_value(double val, bool compact= false);
+  virtual Glib::ustring get_name();
+  virtual Glib::ustring get_short_name();
+  virtual int update_interval();
+  virtual void save(XfceRc *settings_w);
+  virtual void load(XfceRc *settings_ro);
+
+  static bool stats_available();
+  static std::vector<Glib::ustring> current_device_names();
+  static Glib::ustring stat_to_string(
+      const DiskStatsMonitor::Stat &stat, const bool short_ver);
+
+private:
+
+  static const Glib::ustring& diskstats_path;
+
+  /* Determines whether the statistic is to be treated as a straight number or
+   * diffed from its previous value and therefore expressed as change/time */
+  bool convert_to_rate();
+
+  virtual double do_measure();
+
+  /* Reads the diskstats file and returns a vector of each device, containing a
+   * vector of reported stats. Note that unordered_map is C++11 */
+  static std::map<Glib::ustring, std::vector<unsigned long int>> parse_disk_stats();
+
+  guint64 max_value;
+
+  Glib::ustring device_name;
+  Stat stat_to_monitor;
+  double previous_value;
+};
+
 class NetworkLoadMonitor: public Monitor
 {
 public:
diff --git a/src/ui.glade b/src/ui.glade
index 2c0897f..c96a540 100644
--- a/src/ui.glade
+++ b/src/ui.glade
@@ -570,6 +570,8 @@ view</property>
                                   <object class="GtkComboBox" id="disk_stats_device_combobox">
                                     <property name="visible">True</property>
                                     <property name="can_focus">False</property>
+                                    <property name="has_entry">True</property>
+                                    <property name="entry_text_column">0</property>
                                   </object>
                                   <packing>
                                     <property name="expand">True</property>

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the Xfce4-commits mailing list