From ccdeafdefbd43074da6dfe4bfa9d2f24c374fad0 Mon Sep 17 00:00:00 2001 From: Sergey Bugaev Date: Sun, 18 Aug 2019 16:18:20 +0300 Subject: [PATCH] SystemMonitor: Add a devices tab This tab combines info from /proc/devices with device file paths from /dev. --- Applications/SystemMonitor/DevicesModel.cpp | 151 ++++++++++++++++++++ Applications/SystemMonitor/DevicesModel.h | 44 ++++++ Applications/SystemMonitor/Makefile | 1 + Applications/SystemMonitor/main.cpp | 18 +++ 4 files changed, 214 insertions(+) create mode 100644 Applications/SystemMonitor/DevicesModel.cpp create mode 100644 Applications/SystemMonitor/DevicesModel.h diff --git a/Applications/SystemMonitor/DevicesModel.cpp b/Applications/SystemMonitor/DevicesModel.cpp new file mode 100644 index 00000000000..0e0ff6bab68 --- /dev/null +++ b/Applications/SystemMonitor/DevicesModel.cpp @@ -0,0 +1,151 @@ +#include "DevicesModel.h" +#include +#include +#include +#include +#include +#include + +NonnullRefPtr DevicesModel::create() +{ + return adopt(*new DevicesModel); +} + +DevicesModel::DevicesModel() +{ +} + +DevicesModel::~DevicesModel() +{ +} + +int DevicesModel::row_count(const GModelIndex&) const +{ + return m_devices.size(); +} + +int DevicesModel::column_count(const GModelIndex&) const +{ + return Column::__Count; +} + +String DevicesModel::column_name(int column) const +{ + switch (column) { + case Column::Device: + return "Device"; + case Column::Major: + return "Major"; + case Column::Minor: + return "Minor"; + case Column::ClassName: + return "Class"; + case Column::Type: + return "Type"; + default: + ASSERT_NOT_REACHED(); + } +} + +GModel::ColumnMetadata DevicesModel::column_metadata(int column) const +{ + switch (column) { + case Column::Device: + return { 70, TextAlignment::CenterLeft }; + case Column::Major: + return { 32, TextAlignment::CenterRight }; + case Column::Minor: + return { 32, TextAlignment::CenterRight }; + case Column::ClassName: + return { 120, TextAlignment::CenterLeft }; + case Column::Type: + return { 120, TextAlignment::CenterLeft }; + default: + ASSERT_NOT_REACHED(); + } +} + +GVariant DevicesModel::data(const GModelIndex& index, Role) const +{ + ASSERT(is_valid(index)); + + const DeviceInfo& device = m_devices[index.row()]; + switch (index.column()) { + case Column::Device: + return device.path; + case Column::Major: + return device.major; + case Column::Minor: + return device.minor; + case Column::ClassName: + return device.class_name; + case Column::Type: + switch (device.type) { + case DeviceInfo::Type::Block: + return "Block"; + case DeviceInfo::Type::Character: + return "Character"; + default: + ASSERT_NOT_REACHED(); + } + default: + ASSERT_NOT_REACHED(); + } +} + +void DevicesModel::update() +{ + CFile proc_devices { "/proc/devices" }; + if (!proc_devices.open(CIODevice::OpenMode::ReadOnly)) + ASSERT_NOT_REACHED(); + + auto json = JsonValue::from_string(proc_devices.read_all()).as_array(); + + m_devices.clear(); + json.for_each([this](auto& value) { + JsonObject device = value.as_object(); + DeviceInfo device_info; + + device_info.major = device.get("major").to_uint(); + device_info.minor = device.get("minor").to_uint(); + device_info.class_name = device.get("class_name").to_string(); + + String type_str = device.get("type").to_string(); + if (type_str == "block") + device_info.type = DeviceInfo::Type::Block; + else if (type_str == "character") + device_info.type = DeviceInfo::Type::Character; + else + ASSERT_NOT_REACHED(); + + m_devices.append(move(device_info)); + }); + + auto fill_in_paths_from_dir = [this](const String& dir) { + CDirIterator dir_iter { dir, CDirIterator::Flags::SkipDots }; + while (dir_iter.has_next()) { + auto name = dir_iter.next_path(); + auto path = String::format("%s/%s", dir.characters(), name.characters()); + struct stat statbuf; + if (lstat(path.characters(), &statbuf) != 0) { + ASSERT_NOT_REACHED(); + } + if (!S_ISBLK(statbuf.st_mode) && !S_ISCHR(statbuf.st_mode)) + continue; + unsigned major = ::major(statbuf.st_rdev); + unsigned minor = ::minor(statbuf.st_rdev); + + auto it = m_devices.find([major, minor](auto& device_info) { + return device_info.major == major && device_info.minor == minor; + }); + if (it != m_devices.end()) { + (*it).path = move(path); + } + } + }; + + fill_in_paths_from_dir("/dev"); + fill_in_paths_from_dir("/dev/pts"); + + did_update(); +} diff --git a/Applications/SystemMonitor/DevicesModel.h b/Applications/SystemMonitor/DevicesModel.h new file mode 100644 index 00000000000..ab1b36729bb --- /dev/null +++ b/Applications/SystemMonitor/DevicesModel.h @@ -0,0 +1,44 @@ +#pragma once + +#include +#include +#include + +class DevicesModel final : public GModel { +public: + enum Column { + Device = 0, + Major, + Minor, + ClassName, + Type, + __Count + }; + + virtual ~DevicesModel() override; + static NonnullRefPtr create(); + + virtual int row_count(const GModelIndex&) const override; + virtual int column_count(const GModelIndex&) const override; + virtual String column_name(int column) const override; + virtual ColumnMetadata column_metadata(int column) const override; + virtual GVariant data(const GModelIndex&, Role = Role::Display) const override; + virtual void update() override; + +private: + DevicesModel(); + + struct DeviceInfo { + String path; + unsigned major; + unsigned minor; + String class_name; + enum Type { + Block, + Character + }; + Type type; + }; + + Vector m_devices; +}; diff --git a/Applications/SystemMonitor/Makefile b/Applications/SystemMonitor/Makefile index 9c760c378b7..69a15d042b9 100644 --- a/Applications/SystemMonitor/Makefile +++ b/Applications/SystemMonitor/Makefile @@ -2,6 +2,7 @@ include ../../Makefile.common OBJS = \ ProcessModel.o \ + DevicesModel.o \ ProcessTableView.o \ MemoryStatsWidget.o \ GraphWidget.o \ diff --git a/Applications/SystemMonitor/main.cpp b/Applications/SystemMonitor/main.cpp index 8bd2b2551ea..45dfc0f93a3 100644 --- a/Applications/SystemMonitor/main.cpp +++ b/Applications/SystemMonitor/main.cpp @@ -1,3 +1,4 @@ +#include "DevicesModel.h" #include "GraphWidget.h" #include "MemoryStatsWidget.h" #include "NetworkStatisticsWidget.h" @@ -39,6 +40,7 @@ static String human_readable_size(u32 size) static GWidget* build_file_systems_tab(); static GWidget* build_pci_devices_tab(); +static GWidget* build_devices_tab(); int main(int argc, char** argv) { @@ -94,6 +96,8 @@ int main(int argc, char** argv) tabwidget->add_widget("PCI devices", build_pci_devices_tab()); + tabwidget->add_widget("Devices", build_devices_tab()); + auto* network_stats_widget = new NetworkStatisticsWidget(nullptr); tabwidget->add_widget("Network", network_stats_widget); @@ -347,3 +351,17 @@ GWidget* build_pci_devices_tab() return pci_widget; } + +GWidget* build_devices_tab() +{ + auto* devices_widget = new GWidget(nullptr); + devices_widget->set_layout(make(Orientation::Vertical)); + devices_widget->layout()->set_margins({ 4, 4, 4, 4 }); + + auto* devices_table_view = new GTableView(devices_widget); + devices_table_view->set_size_columns_to_fit_content(true); + devices_table_view->set_model(GSortingProxyModel::create(DevicesModel::create())); + devices_table_view->model()->update(); + + return devices_widget; +}