Parcourir la source

Kernel+SystemMonitor: Add fault counters

This patch adds three separate per-process fault counters:

- Inode faults

    An inode fault happens when we've memory-mapped a file from disk
    and we end up having to load 1 page (4KB) of the file into memory.

- Zero faults

    Memory returned by mmap() is lazily zeroed out. Every time we have
    to zero out 1 page, we count a zero fault.

- CoW faults

    VM objects can be shared by multiple mappings that make their own
    unique copy iff they want to modify it. The typical reason here is
    memory shared between a parent and child process.
Andreas Kling il y a 5 ans
Parent
commit
35138437ef

+ 27 - 0
Applications/SystemMonitor/ProcessModel.cpp

@@ -54,6 +54,12 @@ String ProcessModel::column_name(int column) const
         return "Name";
     case Column::Syscalls:
         return "Syscalls";
+    case Column::InodeFaults:
+        return "F:Inode";
+    case Column::ZeroFaults:
+        return "F:Zero";
+    case Column::CowFaults:
+        return "F:CoW";
     default:
         ASSERT_NOT_REACHED();
     }
@@ -82,6 +88,12 @@ GModel::ColumnMetadata ProcessModel::column_metadata(int column) const
         return { 140, TextAlignment::CenterLeft };
     case Column::Syscalls:
         return { 60, TextAlignment::CenterRight };
+    case Column::InodeFaults:
+        return { 60, TextAlignment::CenterRight };
+    case Column::ZeroFaults:
+        return { 60, TextAlignment::CenterRight };
+    case Column::CowFaults:
+        return { 60, TextAlignment::CenterRight };
     default:
         ASSERT_NOT_REACHED();
     }
@@ -131,6 +143,12 @@ GVariant ProcessModel::data(const GModelIndex& index, Role role) const
         // FIXME: GVariant with unsigned?
         case Column::Syscalls:
             return (int)process.current_state.syscall_count;
+        case Column::InodeFaults:
+            return (int)process.current_state.inode_faults;
+        case Column::ZeroFaults:
+            return (int)process.current_state.zero_faults;
+        case Column::CowFaults:
+            return (int)process.current_state.cow_faults;
         }
         ASSERT_NOT_REACHED();
         return {};
@@ -175,6 +193,12 @@ GVariant ProcessModel::data(const GModelIndex& index, Role role) const
         // FIXME: It's weird that GVariant doesn't support unsigned ints. Should it?
         case Column::Syscalls:
             return (int)process.current_state.syscall_count;
+        case Column::InodeFaults:
+            return (int)process.current_state.inode_faults;
+        case Column::ZeroFaults:
+            return (int)process.current_state.zero_faults;
+        case Column::CowFaults:
+            return (int)process.current_state.cow_faults;
         }
     }
 
@@ -198,6 +222,9 @@ void ProcessModel::update()
         state.user = it.value.username;
         state.priority = it.value.priority;
         state.syscall_count = it.value.syscall_count;
+        state.inode_faults = it.value.inode_faults;
+        state.zero_faults = it.value.zero_faults;
+        state.cow_faults = it.value.cow_faults;
         state.state = it.value.state;
         state.name = it.value.name;
         state.amount_virtual = it.value.amount_virtual;

+ 6 - 0
Applications/SystemMonitor/ProcessModel.h

@@ -21,6 +21,9 @@ public:
         Virtual,
         Physical,
         Syscalls,
+        InodeFaults,
+        ZeroFaults,
+        CowFaults,
         __Count
     };
 
@@ -49,6 +52,9 @@ private:
         size_t amount_virtual;
         size_t amount_resident;
         unsigned syscall_count;
+        unsigned inode_faults;
+        unsigned zero_faults;
+        unsigned cow_faults;
         float cpu_percent;
         int icon_id;
     };

+ 3 - 0
Kernel/FileSystem/ProcFS.cpp

@@ -685,6 +685,9 @@ Optional<KBuffer> procfs$all(InodeIdentifier)
         process_object.add("ticks", process.main_thread().ticks());
         process_object.add("priority", to_string(process.priority()));
         process_object.add("syscall_count", process.syscall_count());
+        process_object.add("inode_faults", process.inode_faults());
+        process_object.add("zero_faults", process.zero_faults());
+        process_object.add("cow_faults", process.cow_faults());
         process_object.add("icon_id", process.icon_id());
     };
     build_process(*Scheduler::colonel());

+ 9 - 0
Kernel/Process.h

@@ -291,6 +291,12 @@ public:
 
     unsigned syscall_count() const { return m_syscall_count; }
     void did_syscall() { ++m_syscall_count; }
+    unsigned inode_faults() const { return m_inode_faults; }
+    void did_inode_fault() { ++m_inode_faults; }
+    unsigned zero_faults() const { return m_zero_faults; }
+    void did_zero_fault() { ++m_zero_faults; }
+    unsigned cow_faults() const { return m_cow_faults; }
+    void did_cow_fault() { ++m_cow_faults; }
 
     const ELFLoader* elf_loader() const { return m_elf_loader.ptr(); }
 
@@ -369,6 +375,9 @@ private:
     int m_next_tid { 0 };
 
     unsigned m_syscall_count { 0 };
+    unsigned m_inode_faults { 0 };
+    unsigned m_zero_faults { 0 };
+    unsigned m_cow_faults { 0 };
 
     RefPtr<ProcessTracer> m_tracer;
     OwnPtr<ELFLoader> m_elf_loader;

+ 11 - 0
Kernel/VM/MemoryManager.cpp

@@ -296,6 +296,7 @@ bool MemoryManager::zero_page(Region& region, unsigned page_index_in_region)
     sti();
     LOCKER(vmo.m_paging_lock);
     cli();
+
     if (!vmo_page.is_null()) {
 #ifdef PAGE_FAULT_DEBUG
         dbgprintf("MM: zero_page() but page already present. Fine with me!\n");
@@ -303,6 +304,10 @@ bool MemoryManager::zero_page(Region& region, unsigned page_index_in_region)
         remap_region_page(region, page_index_in_region);
         return true;
     }
+
+    if (current)
+        current->process().did_zero_fault();
+
     auto physical_page = allocate_user_physical_page(ShouldZeroFill::Yes);
 #ifdef PAGE_FAULT_DEBUG
     dbgprintf("      >> ZERO P%p\n", physical_page->paddr().get());
@@ -325,6 +330,9 @@ bool MemoryManager::copy_on_write(Region& region, unsigned page_index_in_region)
         return true;
     }
 
+    if (current)
+        current->process().did_cow_fault();
+
 #ifdef PAGE_FAULT_DEBUG
     dbgprintf("    >> It's a COW page and it's time to COW!\n");
 #endif
@@ -367,6 +375,9 @@ bool MemoryManager::page_in_from_inode(Region& region, unsigned page_index_in_re
         return true;
     }
 
+    if (current)
+        current->process().did_inode_fault();
+
 #ifdef MM_DEBUG
     dbgprintf("MM: page_in_from_inode ready to read from inode\n");
 #endif

+ 3 - 0
Libraries/LibCore/CProcessStatisticsReader.cpp

@@ -43,6 +43,9 @@ HashMap<pid_t, CProcessStatistics> CProcessStatisticsReader::get_all()
         process.ticks = process_object.get("ticks").to_u32();
         process.priority = process_object.get("priority").to_string();
         process.syscall_count = process_object.get("syscall_count").to_u32();
+        process.inode_faults = process_object.get("inode_faults").to_u32();
+        process.zero_faults = process_object.get("zero_faults").to_u32();
+        process.cow_faults = process_object.get("cow_faults").to_u32();
         process.icon_id = process_object.get("icon_id").to_int();
 
         // and synthetic data last

+ 3 - 0
Libraries/LibCore/CProcessStatisticsReader.h

@@ -24,6 +24,9 @@ struct CProcessStatistics {
     unsigned ticks;
     String priority;
     unsigned syscall_count;
+    unsigned inode_faults;
+    unsigned zero_faults;
+    unsigned cow_faults;
     int icon_id;
 
     // synthetic