From 5a45376180fe589622a169828bb030eb929a509d Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Sun, 1 Dec 2019 17:36:06 +0100 Subject: [PATCH] Kernel+SystemMonitor: Log amounts of I/O per thread This patch adds these I/O counters to each thread: - (Inode) file read bytes - (Inode) file write bytes - Unix socket read bytes - Unix socket write bytes - IPv4 socket read bytes - IPv4 socket write bytes These are then exposed in /proc/all and seen in SystemMonitor. --- Applications/SystemMonitor/ProcessModel.cpp | 54 +++++++++++++++++++ Applications/SystemMonitor/ProcessModel.h | 12 +++++ Kernel/FileSystem/InodeFile.cpp | 13 +++-- Kernel/FileSystem/ProcFS.cpp | 6 +++ Kernel/Net/IPv4Socket.cpp | 10 +++- Kernel/Net/LocalSocket.cpp | 29 ++++++---- Kernel/Net/LocalSocket.h | 3 +- Kernel/Thread.h | 48 +++++++++++++++++ .../LibCore/CProcessStatisticsReader.cpp | 7 ++- Libraries/LibCore/CProcessStatisticsReader.h | 6 +++ 10 files changed, 171 insertions(+), 17 deletions(-) diff --git a/Applications/SystemMonitor/ProcessModel.cpp b/Applications/SystemMonitor/ProcessModel.cpp index 9dbaf99807..d6a6b782de 100644 --- a/Applications/SystemMonitor/ProcessModel.cpp +++ b/Applications/SystemMonitor/ProcessModel.cpp @@ -71,6 +71,18 @@ String ProcessModel::column_name(int column) const return "F:Zero"; case Column::CowFaults: return "F:CoW"; + case Column::IPv4SocketReadBytes: + return "IPv4 In"; + case Column::IPv4SocketWriteBytes: + return "IPv4 Out"; + case Column::UnixSocketReadBytes: + return "Unix In"; + case Column::UnixSocketWriteBytes: + return "Unix Out"; + case Column::FileReadBytes: + return "File In"; + case Column::FileWriteBytes: + return "File Out"; default: ASSERT_NOT_REACHED(); } @@ -107,6 +119,18 @@ GModel::ColumnMetadata ProcessModel::column_metadata(int column) const return { 60, TextAlignment::CenterRight }; case Column::CowFaults: return { 60, TextAlignment::CenterRight }; + case Column::FileReadBytes: + return { 60, TextAlignment::CenterRight }; + case Column::FileWriteBytes: + return { 60, TextAlignment::CenterRight }; + case Column::UnixSocketReadBytes: + return { 60, TextAlignment::CenterRight }; + case Column::UnixSocketWriteBytes: + return { 60, TextAlignment::CenterRight }; + case Column::IPv4SocketReadBytes: + return { 60, TextAlignment::CenterRight }; + case Column::IPv4SocketWriteBytes: + return { 60, TextAlignment::CenterRight }; default: ASSERT_NOT_REACHED(); } @@ -164,6 +188,18 @@ GVariant ProcessModel::data(const GModelIndex& index, Role role) const return (int)thread.current_state.zero_faults; case Column::CowFaults: return (int)thread.current_state.cow_faults; + case Column::IPv4SocketReadBytes: + return (int)thread.current_state.ipv4_socket_read_bytes; + case Column::IPv4SocketWriteBytes: + return (int)thread.current_state.ipv4_socket_write_bytes; + case Column::UnixSocketReadBytes: + return (int)thread.current_state.unix_socket_read_bytes; + case Column::UnixSocketWriteBytes: + return (int)thread.current_state.unix_socket_write_bytes; + case Column::FileReadBytes: + return (int)thread.current_state.file_read_bytes; + case Column::FileWriteBytes: + return (int)thread.current_state.file_write_bytes; } ASSERT_NOT_REACHED(); return {}; @@ -216,6 +252,18 @@ GVariant ProcessModel::data(const GModelIndex& index, Role role) const return (int)thread.current_state.zero_faults; case Column::CowFaults: return (int)thread.current_state.cow_faults; + case Column::IPv4SocketReadBytes: + return (int)thread.current_state.ipv4_socket_read_bytes; + case Column::IPv4SocketWriteBytes: + return (int)thread.current_state.ipv4_socket_write_bytes; + case Column::UnixSocketReadBytes: + return (int)thread.current_state.unix_socket_read_bytes; + case Column::UnixSocketWriteBytes: + return (int)thread.current_state.unix_socket_write_bytes; + case Column::FileReadBytes: + return (int)thread.current_state.file_read_bytes; + case Column::FileWriteBytes: + return (int)thread.current_state.file_write_bytes; } } @@ -241,6 +289,12 @@ void ProcessModel::update() state.inode_faults = thread.inode_faults; state.zero_faults = thread.zero_faults; state.cow_faults = thread.cow_faults; + state.unix_socket_read_bytes = thread.unix_socket_read_bytes; + state.unix_socket_write_bytes = thread.unix_socket_write_bytes; + state.ipv4_socket_read_bytes = thread.ipv4_socket_read_bytes; + state.ipv4_socket_write_bytes = thread.ipv4_socket_write_bytes; + state.file_read_bytes = thread.file_read_bytes; + state.file_write_bytes = thread.file_write_bytes; state.name = it.value.name; state.amount_virtual = it.value.amount_virtual; state.amount_resident = it.value.amount_resident; diff --git a/Applications/SystemMonitor/ProcessModel.h b/Applications/SystemMonitor/ProcessModel.h index c6365d109f..bb22d459cb 100644 --- a/Applications/SystemMonitor/ProcessModel.h +++ b/Applications/SystemMonitor/ProcessModel.h @@ -34,6 +34,12 @@ public: InodeFaults, ZeroFaults, CowFaults, + FileReadBytes, + FileWriteBytes, + UnixSocketReadBytes, + UnixSocketWriteBytes, + IPv4SocketReadBytes, + IPv4SocketWriteBytes, __Count }; @@ -68,6 +74,12 @@ private: unsigned inode_faults; unsigned zero_faults; unsigned cow_faults; + unsigned unix_socket_read_bytes; + unsigned unix_socket_write_bytes; + unsigned ipv4_socket_read_bytes; + unsigned ipv4_socket_write_bytes; + unsigned file_read_bytes; + unsigned file_write_bytes; float cpu_percent; int icon_id; }; diff --git a/Kernel/FileSystem/InodeFile.cpp b/Kernel/FileSystem/InodeFile.cpp index 7ba9814019..d47f42ba2a 100644 --- a/Kernel/FileSystem/InodeFile.cpp +++ b/Kernel/FileSystem/InodeFile.cpp @@ -15,15 +15,20 @@ InodeFile::~InodeFile() ssize_t InodeFile::read(FileDescription& description, u8* buffer, ssize_t count) { - return m_inode->read_bytes(description.offset(), count, buffer, &description); + ssize_t nread = m_inode->read_bytes(description.offset(), count, buffer, &description); + if (nread > 0) + current->did_file_read(nread); + return nread; } ssize_t InodeFile::write(FileDescription& description, const u8* data, ssize_t count) { - ssize_t ret = m_inode->write_bytes(description.offset(), count, data, &description); - if (ret > 0) + ssize_t nwritten = m_inode->write_bytes(description.offset(), count, data, &description); + if (nwritten > 0) { m_inode->set_mtime(kgettimeofday().tv_sec); - return ret; + current->did_file_write(nwritten); + } + return nwritten; } KResultOr InodeFile::mmap(Process& process, FileDescription& description, VirtualAddress preferred_vaddr, size_t offset, size_t size, int prot) diff --git a/Kernel/FileSystem/ProcFS.cpp b/Kernel/FileSystem/ProcFS.cpp index 2f98ae442b..749a169538 100644 --- a/Kernel/FileSystem/ProcFS.cpp +++ b/Kernel/FileSystem/ProcFS.cpp @@ -725,6 +725,12 @@ Optional procfs$all(InodeIdentifier) thread_object.add("inode_faults", thread.inode_faults()); thread_object.add("zero_faults", thread.zero_faults()); thread_object.add("cow_faults", thread.cow_faults()); + thread_object.add("file_read_bytes", thread.file_read_bytes()); + thread_object.add("file_write_bytes", thread.file_write_bytes()); + thread_object.add("unix_socket_read_bytes", thread.unix_socket_read_bytes()); + thread_object.add("unix_socket_write_bytes", thread.unix_socket_write_bytes()); + thread_object.add("ipv4_socket_read_bytes", thread.ipv4_socket_read_bytes()); + thread_object.add("ipv4_socket_write_bytes", thread.ipv4_socket_write_bytes()); return IterationDecision::Continue; }); }; diff --git a/Kernel/Net/IPv4Socket.cpp b/Kernel/Net/IPv4Socket.cpp index 3949fb5692..06d3e21249 100644 --- a/Kernel/Net/IPv4Socket.cpp +++ b/Kernel/Net/IPv4Socket.cpp @@ -206,7 +206,10 @@ ssize_t IPv4Socket::sendto(FileDescription&, const void* data, size_t data_lengt return data_length; } - return protocol_send(data, data_length); + int nsent = protocol_send(data, data_length); + if (nsent > 0) + current->did_ipv4_socket_write(nsent); + return nsent; } ssize_t IPv4Socket::recvfrom(FileDescription& description, void* buffer, size_t buffer_length, int flags, sockaddr* addr, socklen_t* addr_length) @@ -285,7 +288,10 @@ ssize_t IPv4Socket::recvfrom(FileDescription& description, void* buffer, size_t return ipv4_packet.payload_size(); } - return protocol_receive(packet.data.value(), buffer, buffer_length, flags); + int nreceived = protocol_receive(packet.data.value(), buffer, buffer_length, flags); + if (nreceived > 0) + current->did_ipv4_socket_read(nreceived); + return nreceived; } bool IPv4Socket::did_receive(const IPv4Address& source_address, u16 source_port, KBuffer&& packet) diff --git a/Kernel/Net/LocalSocket.cpp b/Kernel/Net/LocalSocket.cpp index babe57a023..d41d445620 100644 --- a/Kernel/Net/LocalSocket.cpp +++ b/Kernel/Net/LocalSocket.cpp @@ -222,15 +222,13 @@ ssize_t LocalSocket::sendto(FileDescription& description, const void* data, size { if (!has_attached_peer(description)) return -EPIPE; - auto role = this->role(description); - if (role == Role::Accepted) - return m_for_client.write((const u8*)data, data_size); - if (role == Role::Connected) - return m_for_server.write((const u8*)data, data_size); - ASSERT_NOT_REACHED(); + ssize_t nwritten = send_buffer_for(description).write((const u8*)data, data_size); + if (nwritten > 0) + current->did_unix_socket_write(nwritten); + return nwritten; } -DoubleBuffer& LocalSocket::buffer_for(FileDescription& description) +DoubleBuffer& LocalSocket::receive_buffer_for(FileDescription& description) { auto role = this->role(description); if (role == Role::Accepted) @@ -240,9 +238,19 @@ DoubleBuffer& LocalSocket::buffer_for(FileDescription& description) ASSERT_NOT_REACHED(); } +DoubleBuffer& LocalSocket::send_buffer_for(FileDescription& description) +{ + auto role = this->role(description); + if (role == Role::Connected) + return m_for_server; + if (role == Role::Accepted) + return m_for_client; + ASSERT_NOT_REACHED(); +} + ssize_t LocalSocket::recvfrom(FileDescription& description, void* buffer, size_t buffer_size, int, sockaddr*, socklen_t*) { - auto& buffer_for_me = buffer_for(description); + auto& buffer_for_me = receive_buffer_for(description); if (!description.is_blocking()) { if (buffer_for_me.is_empty()) { if (!has_attached_peer(description)) @@ -257,7 +265,10 @@ ssize_t LocalSocket::recvfrom(FileDescription& description, void* buffer, size_t if (!has_attached_peer(description) && buffer_for_me.is_empty()) return 0; ASSERT(!buffer_for_me.is_empty()); - return buffer_for_me.read((u8*)buffer, buffer_size); + int nread = buffer_for_me.read((u8*)buffer, buffer_size); + if (nread > 0) + current->did_unix_socket_read(nread); + return nread; } StringView LocalSocket::socket_path() const diff --git a/Kernel/Net/LocalSocket.h b/Kernel/Net/LocalSocket.h index bd529933f1..7677a25e8d 100644 --- a/Kernel/Net/LocalSocket.h +++ b/Kernel/Net/LocalSocket.h @@ -36,7 +36,8 @@ private: virtual bool is_local() const override { return true; } bool has_attached_peer(const FileDescription&) const; static Lockable>& all_sockets(); - DoubleBuffer& buffer_for(FileDescription&); + DoubleBuffer& receive_buffer_for(FileDescription&); + DoubleBuffer& send_buffer_for(FileDescription&); // An open socket file on the filesystem. RefPtr m_file; diff --git a/Kernel/Thread.h b/Kernel/Thread.h index 54a46e4e2d..8f3c044438 100644 --- a/Kernel/Thread.h +++ b/Kernel/Thread.h @@ -363,6 +363,45 @@ public: unsigned cow_faults() const { return m_cow_faults; } void did_cow_fault() { ++m_cow_faults; } + unsigned file_read_bytes() const { return m_file_read_bytes; } + unsigned file_write_bytes() const { return m_file_write_bytes; } + + void did_file_read(unsigned bytes) + { + m_file_read_bytes += bytes; + } + + void did_file_write(unsigned bytes) + { + m_file_write_bytes += bytes; + } + + unsigned unix_socket_read_bytes() const { return m_unix_socket_read_bytes; } + unsigned unix_socket_write_bytes() const { return m_unix_socket_write_bytes; } + + void did_unix_socket_read(unsigned bytes) + { + m_unix_socket_read_bytes += bytes; + } + + void did_unix_socket_write(unsigned bytes) + { + m_unix_socket_write_bytes += bytes; + } + + unsigned ipv4_socket_read_bytes() const { return m_ipv4_socket_read_bytes; } + unsigned ipv4_socket_write_bytes() const { return m_ipv4_socket_write_bytes; } + + void did_ipv4_socket_read(unsigned bytes) + { + m_ipv4_socket_read_bytes += bytes; + } + + void did_ipv4_socket_write(unsigned bytes) + { + m_ipv4_socket_write_bytes += bytes; + } + Thread* clone(Process&); template @@ -416,6 +455,15 @@ private: unsigned m_zero_faults { 0 }; unsigned m_cow_faults { 0 }; + unsigned m_file_read_bytes { 0 }; + unsigned m_file_write_bytes { 0 }; + + unsigned m_unix_socket_read_bytes { 0 }; + unsigned m_unix_socket_write_bytes { 0 }; + + unsigned m_ipv4_socket_read_bytes { 0 }; + unsigned m_ipv4_socket_write_bytes { 0 }; + FPUState* m_fpu_state { nullptr }; State m_state { Invalid }; ThreadPriority m_priority { ThreadPriority::Normal }; diff --git a/Libraries/LibCore/CProcessStatisticsReader.cpp b/Libraries/LibCore/CProcessStatisticsReader.cpp index 39f8db097e..39db980ee8 100644 --- a/Libraries/LibCore/CProcessStatisticsReader.cpp +++ b/Libraries/LibCore/CProcessStatisticsReader.cpp @@ -53,6 +53,12 @@ HashMap CProcessStatisticsReader::get_all() thread.inode_faults = thread_object.get("inode_faults").to_u32(); thread.zero_faults = thread_object.get("zero_faults").to_u32(); thread.cow_faults = thread_object.get("cow_faults").to_u32(); + thread.unix_socket_read_bytes = thread_object.get("unix_socket_read_bytes").to_u32(); + thread.unix_socket_write_bytes = thread_object.get("unix_socket_write_bytes").to_u32(); + thread.ipv4_socket_read_bytes = thread_object.get("ipv4_socket_read_bytes").to_u32(); + thread.ipv4_socket_write_bytes = thread_object.get("ipv4_socket_write_bytes").to_u32(); + thread.file_read_bytes = thread_object.get("file_read_bytes").to_u32(); + thread.file_write_bytes = thread_object.get("file_write_bytes").to_u32(); process.threads.append(move(thread)); }); @@ -78,4 +84,3 @@ String CProcessStatisticsReader::username_from_uid(uid_t uid) return (*it).value; return String::number(uid); } - diff --git a/Libraries/LibCore/CProcessStatisticsReader.h b/Libraries/LibCore/CProcessStatisticsReader.h index bb6da21d31..05e9e9659e 100644 --- a/Libraries/LibCore/CProcessStatisticsReader.h +++ b/Libraries/LibCore/CProcessStatisticsReader.h @@ -12,6 +12,12 @@ struct CThreadStatistics { unsigned inode_faults; unsigned zero_faults; unsigned cow_faults; + unsigned unix_socket_read_bytes; + unsigned unix_socket_write_bytes; + unsigned ipv4_socket_read_bytes; + unsigned ipv4_socket_write_bytes; + unsigned file_read_bytes; + unsigned file_write_bytes; String state; String priority; };