From c74cde918a5ef031d5f01c663212b1c909564cf4 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Sun, 29 Dec 2019 12:45:58 +0100 Subject: [PATCH] Kernel+SystemMonitor: Expose amount of per-process clean inode memory This is memory that's loaded from an inode (file) but not modified in memory, so still identical to what's on disk. This kind of memory can be freed and reloaded transparently from disk if needed. --- Applications/SystemMonitor/ProcessModel.cpp | 9 +++++++++ Applications/SystemMonitor/ProcessModel.h | 2 ++ Kernel/FileSystem/ProcFS.cpp | 1 + Kernel/Process.cpp | 16 ++++++++++++++++ Kernel/Process.h | 1 + Kernel/VM/InodeVMObject.cpp | 10 ++++++++++ Kernel/VM/InodeVMObject.h | 1 + Libraries/LibCore/CProcessStatisticsReader.cpp | 1 + Libraries/LibCore/CProcessStatisticsReader.h | 1 + 9 files changed, 42 insertions(+) diff --git a/Applications/SystemMonitor/ProcessModel.cpp b/Applications/SystemMonitor/ProcessModel.cpp index d84b52e59fe..88fa9659074 100644 --- a/Applications/SystemMonitor/ProcessModel.cpp +++ b/Applications/SystemMonitor/ProcessModel.cpp @@ -61,6 +61,8 @@ String ProcessModel::column_name(int column) const return "Physical"; case Column::DirtyPrivate: return "DirtyP"; + case Column::CleanInode: + return "CleanI"; case Column::PurgeableVolatile: return "Purg:V"; case Column::PurgeableNonvolatile: @@ -115,6 +117,8 @@ GModel::ColumnMetadata ProcessModel::column_metadata(int column) const return { 65, TextAlignment::CenterRight }; case Column::DirtyPrivate: return { 65, TextAlignment::CenterRight }; + case Column::CleanInode: + return { 65, TextAlignment::CenterRight }; case Column::PurgeableVolatile: return { 65, TextAlignment::CenterRight }; case Column::PurgeableNonvolatile: @@ -189,6 +193,8 @@ GVariant ProcessModel::data(const GModelIndex& index, Role role) const return (int)thread.current_state.amount_resident; case Column::DirtyPrivate: return (int)thread.current_state.amount_dirty_private; + case Column::CleanInode: + return (int)thread.current_state.amount_clean_inode; case Column::PurgeableVolatile: return (int)thread.current_state.amount_purgeable_volatile; case Column::PurgeableNonvolatile: @@ -258,6 +264,8 @@ GVariant ProcessModel::data(const GModelIndex& index, Role role) const return pretty_byte_size(thread.current_state.amount_resident); case Column::DirtyPrivate: return pretty_byte_size(thread.current_state.amount_dirty_private); + case Column::CleanInode: + return pretty_byte_size(thread.current_state.amount_clean_inode); case Column::PurgeableVolatile: return pretty_byte_size(thread.current_state.amount_purgeable_volatile); case Column::PurgeableNonvolatile: @@ -320,6 +328,7 @@ void ProcessModel::update() state.amount_virtual = it.value.amount_virtual; state.amount_resident = it.value.amount_resident; state.amount_dirty_private = it.value.amount_dirty_private; + state.amount_clean_inode = it.value.amount_clean_inode; state.amount_purgeable_volatile = it.value.amount_purgeable_volatile; state.amount_purgeable_nonvolatile = it.value.amount_purgeable_nonvolatile; state.icon_id = it.value.icon_id; diff --git a/Applications/SystemMonitor/ProcessModel.h b/Applications/SystemMonitor/ProcessModel.h index e8a38d59480..cccedbcb3ac 100644 --- a/Applications/SystemMonitor/ProcessModel.h +++ b/Applications/SystemMonitor/ProcessModel.h @@ -31,6 +31,7 @@ public: Virtual, Physical, DirtyPrivate, + CleanInode, PurgeableVolatile, PurgeableNonvolatile, Syscalls, @@ -74,6 +75,7 @@ private: size_t amount_virtual; size_t amount_resident; size_t amount_dirty_private; + size_t amount_clean_inode; size_t amount_purgeable_volatile; size_t amount_purgeable_nonvolatile; unsigned syscall_count; diff --git a/Kernel/FileSystem/ProcFS.cpp b/Kernel/FileSystem/ProcFS.cpp index 00aacacfaf2..a3ea6c78ae3 100644 --- a/Kernel/FileSystem/ProcFS.cpp +++ b/Kernel/FileSystem/ProcFS.cpp @@ -754,6 +754,7 @@ Optional procfs$all(InodeIdentifier) process_object.add("amount_virtual", (u32)process.amount_virtual()); process_object.add("amount_resident", (u32)process.amount_resident()); process_object.add("amount_dirty_private", (u32)process.amount_dirty_private()); + process_object.add("amount_clean_inode", (u32)process.amount_clean_inode()); process_object.add("amount_shared", (u32)process.amount_shared()); process_object.add("amount_purgeable_volatile", (u32)process.amount_purgeable_volatile()); process_object.add("amount_purgeable_nonvolatile", (u32)process.amount_purgeable_nonvolatile()); diff --git a/Kernel/Process.cpp b/Kernel/Process.cpp index dd7f6f1f711..91d870fd6d4 100644 --- a/Kernel/Process.cpp +++ b/Kernel/Process.cpp @@ -2504,6 +2504,9 @@ void Process::die() size_t Process::amount_dirty_private() const { + // FIXME: This gets a bit more complicated for Regions sharing the same underlying VMObject. + // The main issue I'm thinking of is when the VMObject has physical pages that none of the Regions are mapping. + // That's probably a situation that needs to be looked at in general. size_t amount = 0; for (auto& region : m_regions) { if (!region.is_shared()) @@ -2512,6 +2515,19 @@ size_t Process::amount_dirty_private() const return amount; } +size_t Process::amount_clean_inode() const +{ + HashTable vmobjects; + for (auto& region : m_regions) { + if (region.vmobject().is_inode()) + vmobjects.set(&static_cast(region.vmobject())); + } + size_t amount = 0; + for (auto& vmobject : vmobjects) + amount += vmobject->amount_clean(); + return amount; +} + size_t Process::amount_virtual() const { size_t amount = 0; diff --git a/Kernel/Process.h b/Kernel/Process.h index d4ad159dae4..40fa865c15d 100644 --- a/Kernel/Process.h +++ b/Kernel/Process.h @@ -272,6 +272,7 @@ public: int number_of_open_file_descriptors() const; int max_open_file_descriptors() const { return m_max_open_file_descriptors; } + size_t amount_clean_inode() const; size_t amount_dirty_private() const; size_t amount_virtual() const; size_t amount_resident() const; diff --git a/Kernel/VM/InodeVMObject.cpp b/Kernel/VM/InodeVMObject.cpp index 5edc9b3c20d..65c11779883 100644 --- a/Kernel/VM/InodeVMObject.cpp +++ b/Kernel/VM/InodeVMObject.cpp @@ -36,6 +36,16 @@ InodeVMObject::~InodeVMObject() ASSERT(inode().vmobject() == this); } +size_t InodeVMObject::amount_clean() const +{ + size_t count = 0; + for (int i = 0; i < m_dirty_pages.size(); ++i) { + if (!m_dirty_pages.get(i) && m_physical_pages[i]) + ++count; + } + return count * PAGE_SIZE; +} + size_t InodeVMObject::amount_dirty() const { size_t count = 0; diff --git a/Kernel/VM/InodeVMObject.h b/Kernel/VM/InodeVMObject.h index 56e8b344d63..e4e44cff7fa 100644 --- a/Kernel/VM/InodeVMObject.h +++ b/Kernel/VM/InodeVMObject.h @@ -17,6 +17,7 @@ public: void inode_size_changed(Badge, size_t old_size, size_t new_size); size_t amount_dirty() const; + size_t amount_clean() const; private: explicit InodeVMObject(Inode&); diff --git a/Libraries/LibCore/CProcessStatisticsReader.cpp b/Libraries/LibCore/CProcessStatisticsReader.cpp index 4e0adc8e135..cfd8f0b40c5 100644 --- a/Libraries/LibCore/CProcessStatisticsReader.cpp +++ b/Libraries/LibCore/CProcessStatisticsReader.cpp @@ -39,6 +39,7 @@ HashMap CProcessStatisticsReader::get_all() process.amount_resident = process_object.get("amount_resident").to_u32(); process.amount_shared = process_object.get("amount_shared").to_u32(); process.amount_dirty_private = process_object.get("amount_dirty_private").to_u32(); + process.amount_clean_inode = process_object.get("amount_clean_inode").to_u32(); process.amount_purgeable_volatile = process_object.get("amount_purgeable_volatile").to_u32(); process.amount_purgeable_nonvolatile = process_object.get("amount_purgeable_nonvolatile").to_u32(); process.icon_id = process_object.get("icon_id").to_int(); diff --git a/Libraries/LibCore/CProcessStatisticsReader.h b/Libraries/LibCore/CProcessStatisticsReader.h index 34b353d85f4..a7b740d8f92 100644 --- a/Libraries/LibCore/CProcessStatisticsReader.h +++ b/Libraries/LibCore/CProcessStatisticsReader.h @@ -40,6 +40,7 @@ struct CProcessStatistics { size_t amount_resident; size_t amount_shared; size_t amount_dirty_private; + size_t amount_clean_inode; size_t amount_purgeable_volatile; size_t amount_purgeable_nonvolatile; int icon_id;