[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