summaryrefslogtreecommitdiff
path: root/Applications/SystemMonitor
diff options
context:
space:
mode:
authorAndreas Kling <awesomekling@gmail.com>2019-11-26 21:25:45 +0100
committerAndreas Kling <awesomekling@gmail.com>2019-11-26 21:37:30 +0100
commit712ae73581a1b44b36654824229f9cc475381c34 (patch)
treebab8587303236eabeef92afce5fe2cad35c9c635 /Applications/SystemMonitor
parent86a9a52355905e4ac71730dba5c325498fd05177 (diff)
downloadserenity-712ae73581a1b44b36654824229f9cc475381c34.zip
Kernel: Expose per-thread information in /proc/all
Previously it was not possible to see what each thread in a process was up to, or how much CPU it was consuming. This patch fixes that. SystemMonitor and "top" now show threads instead of just processes. "ps" is gonna need some more fixing, but it at least builds for now. Fixes #66.
Diffstat (limited to 'Applications/SystemMonitor')
-rw-r--r--Applications/SystemMonitor/ProcessModel.cpp144
-rw-r--r--Applications/SystemMonitor/ProcessModel.h32
2 files changed, 103 insertions, 73 deletions
diff --git a/Applications/SystemMonitor/ProcessModel.cpp b/Applications/SystemMonitor/ProcessModel.cpp
index ec79fa0c99..2445976ade 100644
--- a/Applications/SystemMonitor/ProcessModel.cpp
+++ b/Applications/SystemMonitor/ProcessModel.cpp
@@ -47,6 +47,8 @@ String ProcessModel::column_name(int column) const
return "";
case Column::PID:
return "PID";
+ case Column::TID:
+ return "TID";
case Column::State:
return "State";
case Column::User:
@@ -81,6 +83,8 @@ GModel::ColumnMetadata ProcessModel::column_metadata(int column) const
return { 16, TextAlignment::CenterLeft };
case Column::PID:
return { 32, TextAlignment::CenterRight };
+ case Column::TID:
+ return { 32, TextAlignment::CenterRight };
case Column::State:
return { 75, TextAlignment::CenterLeft };
case Column::Priority:
@@ -117,47 +121,49 @@ GVariant ProcessModel::data(const GModelIndex& index, Role role) const
{
ASSERT(is_valid(index));
- auto it = m_processes.find(m_pids[index.row()]);
- auto& process = *(*it).value;
+ auto it = m_threads.find(m_pids[index.row()]);
+ auto& thread = *(*it).value;
if (role == Role::Sort) {
switch (index.column()) {
case Column::Icon:
return 0;
case Column::PID:
- return process.current_state.pid;
+ return thread.current_state.pid;
+ case Column::TID:
+ return thread.current_state.tid;
case Column::State:
- return process.current_state.state;
+ return thread.current_state.state;
case Column::User:
- return process.current_state.user;
+ return thread.current_state.user;
case Column::Priority:
- if (process.current_state.priority == "Idle")
+ if (thread.current_state.priority == "Idle")
return 0;
- if (process.current_state.priority == "Low")
+ if (thread.current_state.priority == "Low")
return 1;
- if (process.current_state.priority == "Normal")
+ if (thread.current_state.priority == "Normal")
return 2;
- if (process.current_state.priority == "High")
+ if (thread.current_state.priority == "High")
return 3;
ASSERT_NOT_REACHED();
return 3;
case Column::Virtual:
- return (int)process.current_state.amount_virtual;
+ return (int)thread.current_state.amount_virtual;
case Column::Physical:
- return (int)process.current_state.amount_resident;
+ return (int)thread.current_state.amount_resident;
case Column::CPU:
- return process.current_state.cpu_percent;
+ return thread.current_state.cpu_percent;
case Column::Name:
- return process.current_state.name;
+ return thread.current_state.name;
// FIXME: GVariant with unsigned?
case Column::Syscalls:
- return (int)process.current_state.syscall_count;
+ return (int)thread.current_state.syscall_count;
case Column::InodeFaults:
- return (int)process.current_state.inode_faults;
+ return (int)thread.current_state.inode_faults;
case Column::ZeroFaults:
- return (int)process.current_state.zero_faults;
+ return (int)thread.current_state.zero_faults;
case Column::CowFaults:
- return (int)process.current_state.cow_faults;
+ return (int)thread.current_state.cow_faults;
}
ASSERT_NOT_REACHED();
return {};
@@ -166,8 +172,8 @@ GVariant ProcessModel::data(const GModelIndex& index, Role role) const
if (role == Role::Display) {
switch (index.column()) {
case Column::Icon:
- if (process.current_state.icon_id != -1) {
- auto icon_buffer = SharedBuffer::create_from_shared_buffer_id(process.current_state.icon_id);
+ if (thread.current_state.icon_id != -1) {
+ auto icon_buffer = SharedBuffer::create_from_shared_buffer_id(thread.current_state.icon_id);
if (icon_buffer) {
auto icon_bitmap = GraphicsBitmap::create_with_shared_buffer(GraphicsBitmap::Format::RGBA32, *icon_buffer, { 16, 16 });
if (icon_bitmap)
@@ -176,38 +182,40 @@ GVariant ProcessModel::data(const GModelIndex& index, Role role) const
}
return *m_generic_process_icon;
case Column::PID:
- return process.current_state.pid;
+ return thread.current_state.pid;
+ case Column::TID:
+ return thread.current_state.tid;
case Column::State:
- return process.current_state.state;
+ return thread.current_state.state;
case Column::User:
- return process.current_state.user;
+ return thread.current_state.user;
case Column::Priority:
- if (process.current_state.priority == "Idle")
+ if (thread.current_state.priority == "Idle")
return String::empty();
- if (process.current_state.priority == "High")
+ if (thread.current_state.priority == "High")
return *m_high_priority_icon;
- if (process.current_state.priority == "Low")
+ if (thread.current_state.priority == "Low")
return *m_low_priority_icon;
- if (process.current_state.priority == "Normal")
+ if (thread.current_state.priority == "Normal")
return *m_normal_priority_icon;
- return process.current_state.priority;
+ return thread.current_state.priority;
case Column::Virtual:
- return pretty_byte_size(process.current_state.amount_virtual);
+ return pretty_byte_size(thread.current_state.amount_virtual);
case Column::Physical:
- return pretty_byte_size(process.current_state.amount_resident);
+ return pretty_byte_size(thread.current_state.amount_resident);
case Column::CPU:
- return process.current_state.cpu_percent;
+ return thread.current_state.cpu_percent;
case Column::Name:
- return process.current_state.name;
+ return thread.current_state.name;
// FIXME: It's weird that GVariant doesn't support unsigned ints. Should it?
case Column::Syscalls:
- return (int)process.current_state.syscall_count;
+ return (int)thread.current_state.syscall_count;
case Column::InodeFaults:
- return (int)process.current_state.inode_faults;
+ return (int)thread.current_state.inode_faults;
case Column::ZeroFaults:
- return (int)process.current_state.zero_faults;
+ return (int)thread.current_state.zero_faults;
case Column::CowFaults:
- return (int)process.current_state.cow_faults;
+ return (int)thread.current_state.cow_faults;
}
}
@@ -219,44 +227,48 @@ void ProcessModel::update()
auto all_processes = CProcessStatisticsReader::get_all();
unsigned last_sum_times_scheduled = 0;
- for (auto& it : m_processes)
+ for (auto& it : m_threads)
last_sum_times_scheduled += it.value->current_state.times_scheduled;
- HashTable<pid_t> live_pids;
+ HashTable<PidAndTid> live_pids;
unsigned sum_times_scheduled = 0;
for (auto& it : all_processes) {
- ProcessState state;
- state.pid = it.value.pid;
- state.times_scheduled = it.value.times_scheduled;
- 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;
- state.amount_resident = it.value.amount_resident;
- state.icon_id = it.value.icon_id;
- sum_times_scheduled += it.value.times_scheduled;
- {
- auto pit = m_processes.find(it.value.pid);
- if (pit == m_processes.end())
- m_processes.set(it.value.pid, make<Process>());
- }
- auto pit = m_processes.find(it.value.pid);
- ASSERT(pit != m_processes.end());
- (*pit).value->previous_state = (*pit).value->current_state;
- (*pit).value->current_state = state;
+ for (auto& thread : it.value.threads) {
+ ThreadState state;
+ state.pid = it.value.pid;
+ state.user = it.value.username;
+ 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.name = it.value.name;
+ state.amount_virtual = it.value.amount_virtual;
+ state.amount_resident = it.value.amount_resident;
+ state.icon_id = it.value.icon_id;
- live_pids.set(it.value.pid);
+ state.tid = thread.tid;
+ state.times_scheduled = thread.times_scheduled;
+ state.priority = thread.priority;
+ state.state = thread.state;
+ sum_times_scheduled += thread.times_scheduled;
+ {
+ auto pit = m_threads.find({ it.value.pid, thread.tid });
+ if (pit == m_threads.end())
+ m_threads.set({ it.value.pid, thread.tid }, make<Thread>());
+ }
+ auto pit = m_threads.find({ it.value.pid, thread.tid });
+ ASSERT(pit != m_threads.end());
+ (*pit).value->previous_state = (*pit).value->current_state;
+ (*pit).value->current_state = state;
+
+ live_pids.set({ it.value.pid, thread.tid });
+ }
}
m_pids.clear();
float total_cpu_percent = 0;
- Vector<pid_t, 16> pids_to_remove;
- for (auto& it : m_processes) {
+ Vector<PidAndTid, 16> pids_to_remove;
+ for (auto& it : m_threads) {
if (!live_pids.contains(it.key)) {
pids_to_remove.append(it.key);
continue;
@@ -264,13 +276,13 @@ void ProcessModel::update()
auto& process = *it.value;
u32 times_scheduled_diff = process.current_state.times_scheduled - process.previous_state.times_scheduled;
process.current_state.cpu_percent = ((float)times_scheduled_diff * 100) / (float)(sum_times_scheduled - last_sum_times_scheduled);
- if (it.key != 0) {
+ if (it.key.pid != 0) {
total_cpu_percent += process.current_state.cpu_percent;
m_pids.append(it.key);
}
}
for (auto pid : pids_to_remove)
- m_processes.remove(pid);
+ m_threads.remove(pid);
if (on_new_cpu_data_point)
on_new_cpu_data_point(total_cpu_percent);
diff --git a/Applications/SystemMonitor/ProcessModel.h b/Applications/SystemMonitor/ProcessModel.h
index fc54488583..c6365d109f 100644
--- a/Applications/SystemMonitor/ProcessModel.h
+++ b/Applications/SystemMonitor/ProcessModel.h
@@ -1,13 +1,22 @@
#pragma once
-#include <AK/String.h>
#include <AK/HashMap.h>
+#include <AK/String.h>
#include <AK/Vector.h>
#include <LibGUI/GModel.h>
#include <unistd.h>
class GraphWidget;
+struct PidAndTid {
+ bool operator==(const PidAndTid& other) const
+ {
+ return pid == other.pid && tid == other.tid;
+ }
+ pid_t pid;
+ int tid;
+};
+
class ProcessModel final : public GModel {
public:
enum Column {
@@ -18,6 +27,7 @@ public:
Priority,
User,
PID,
+ TID,
Virtual,
Physical,
Syscalls,
@@ -44,7 +54,8 @@ public:
private:
ProcessModel();
- struct ProcessState {
+ struct ThreadState {
+ int tid;
pid_t pid;
unsigned times_scheduled;
String name;
@@ -61,16 +72,23 @@ private:
int icon_id;
};
- struct Process {
- ProcessState current_state;
- ProcessState previous_state;
+ struct Thread {
+ ThreadState current_state;
+ ThreadState previous_state;
};
HashMap<uid_t, String> m_usernames;
- HashMap<pid_t, NonnullOwnPtr<Process>> m_processes;
- Vector<pid_t> m_pids;
+ HashMap<PidAndTid, NonnullOwnPtr<Thread>> m_threads;
+ Vector<PidAndTid> m_pids;
RefPtr<GraphicsBitmap> m_generic_process_icon;
RefPtr<GraphicsBitmap> m_high_priority_icon;
RefPtr<GraphicsBitmap> m_low_priority_icon;
RefPtr<GraphicsBitmap> m_normal_priority_icon;
};
+
+namespace AK {
+template<>
+struct Traits<PidAndTid> : public GenericTraits<PidAndTid> {
+ static unsigned hash(const PidAndTid& value) { return pair_int_hash(value.pid, value.tid); }
+};
+}