mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-12-25 15:39:55 +00:00
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.
This commit is contained in:
parent
86a9a52355
commit
712ae73581
Notes:
sideshowbarker
2024-07-19 11:03:13 +09:00
Author: https://github.com/awesomekling Commit: https://github.com/SerenityOS/serenity/commit/712ae73581a
9 changed files with 243 additions and 123 deletions
|
@ -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);
|
||||
|
|
|
@ -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); }
|
||||
};
|
||||
}
|
||||
|
|
|
@ -55,8 +55,8 @@ void ProcessStateWidget::refresh()
|
|||
auto& data = active_process_data.value();
|
||||
|
||||
m_pid_label->set_text(String::format("%s(%d)", data.name.characters(), pid));
|
||||
m_state_label->set_text(data.state);
|
||||
m_cpu_label->set_text(String::format("%d", data.times_scheduled));
|
||||
m_state_label->set_text(data.threads.first().state);
|
||||
m_cpu_label->set_text(String::format("%d", data.threads.first().times_scheduled));
|
||||
m_memory_label->set_text(String::format("%d", data.amount_resident));
|
||||
}
|
||||
|
||||
|
|
|
@ -677,13 +677,11 @@ Optional<KBuffer> procfs$all(InodeIdentifier)
|
|||
auto build_process = [&](const Process& process) {
|
||||
auto process_object = array.add_object();
|
||||
process_object.add("pid", process.pid());
|
||||
process_object.add("times_scheduled", process.main_thread().times_scheduled());
|
||||
process_object.add("pgid", process.tty() ? process.tty()->pgid() : 0);
|
||||
process_object.add("pgp", process.pgid());
|
||||
process_object.add("sid", process.sid());
|
||||
process_object.add("uid", process.uid());
|
||||
process_object.add("gid", process.gid());
|
||||
process_object.add("state", process.main_thread().state_string());
|
||||
process_object.add("ppid", process.ppid());
|
||||
process_object.add("nfds", process.number_of_open_file_descriptors());
|
||||
process_object.add("name", process.name());
|
||||
|
@ -691,13 +689,21 @@ Optional<KBuffer> 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_shared", (u32)process.amount_shared());
|
||||
process_object.add("ticks", process.main_thread().ticks());
|
||||
process_object.add("priority", to_string(process.main_thread().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());
|
||||
auto thread_array = process_object.add_array("threads");
|
||||
process.for_each_thread([&](const Thread& thread) {
|
||||
auto thread_object = thread_array.add_object();
|
||||
thread_object.add("tid", thread.tid());
|
||||
thread_object.add("times_scheduled", thread.times_scheduled());
|
||||
thread_object.add("ticks", thread.ticks());
|
||||
thread_object.add("state", thread.state_string());
|
||||
thread_object.add("priority", to_string(thread.priority()));
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
};
|
||||
build_process(*Scheduler::colonel());
|
||||
for (auto* process : processes)
|
||||
|
|
|
@ -26,13 +26,11 @@ HashMap<pid_t, CProcessStatistics> CProcessStatisticsReader::get_all()
|
|||
|
||||
// kernel data first
|
||||
process.pid = process_object.get("pid").to_u32();
|
||||
process.times_scheduled = process_object.get("times_scheduled").to_u32();
|
||||
process.pgid = process_object.get("pgid").to_u32();
|
||||
process.pgp = process_object.get("pgp").to_u32();
|
||||
process.sid = process_object.get("sid").to_u32();
|
||||
process.uid = process_object.get("uid").to_u32();
|
||||
process.gid = process_object.get("gid").to_u32();
|
||||
process.state = process_object.get("state").to_string();
|
||||
process.ppid = process_object.get("ppid").to_u32();
|
||||
process.nfds = process_object.get("nfds").to_u32();
|
||||
process.name = process_object.get("name").to_string();
|
||||
|
@ -40,14 +38,24 @@ HashMap<pid_t, CProcessStatistics> CProcessStatisticsReader::get_all()
|
|||
process.amount_virtual = process_object.get("amount_virtual").to_u32();
|
||||
process.amount_resident = process_object.get("amount_resident").to_u32();
|
||||
process.amount_shared = process_object.get("amount_shared").to_u32();
|
||||
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();
|
||||
|
||||
auto thread_array = process_object.get("threads").as_array();
|
||||
thread_array.for_each([&](auto& value) {
|
||||
auto& thread_object = value.as_object();
|
||||
CThreadStatistics thread;
|
||||
thread.tid = thread_object.get("tid").to_u32();
|
||||
thread.times_scheduled = thread_object.get("times_scheduled").to_u32();
|
||||
thread.state = thread_object.get("state").to_string();
|
||||
thread.ticks = thread_object.get("ticks").to_u32();
|
||||
thread.priority = thread_object.get("priority").to_string();
|
||||
process.threads.append(move(thread));
|
||||
});
|
||||
|
||||
// and synthetic data last
|
||||
process.username = username_from_uid(process.uid);
|
||||
map.set(process.pid, process);
|
||||
|
|
|
@ -1,19 +1,26 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/String.h>
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/String.h>
|
||||
#include <unistd.h>
|
||||
|
||||
struct CThreadStatistics {
|
||||
int tid;
|
||||
unsigned times_scheduled;
|
||||
unsigned ticks;
|
||||
String state;
|
||||
String priority;
|
||||
};
|
||||
|
||||
struct CProcessStatistics {
|
||||
// Keep this in sync with /proc/all.
|
||||
// From the kernel side:
|
||||
pid_t pid;
|
||||
unsigned times_scheduled;
|
||||
unsigned pgid;
|
||||
unsigned pgp;
|
||||
unsigned sid;
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
String state;
|
||||
pid_t ppid;
|
||||
unsigned nfds;
|
||||
String name;
|
||||
|
@ -21,14 +28,14 @@ struct CProcessStatistics {
|
|||
size_t amount_virtual;
|
||||
size_t amount_resident;
|
||||
size_t amount_shared;
|
||||
unsigned ticks;
|
||||
String priority;
|
||||
unsigned syscall_count;
|
||||
unsigned inode_faults;
|
||||
unsigned zero_faults;
|
||||
unsigned cow_faults;
|
||||
int icon_id;
|
||||
|
||||
Vector<CThreadStatistics> threads;
|
||||
|
||||
// synthetic
|
||||
String username;
|
||||
};
|
||||
|
|
|
@ -44,10 +44,12 @@ void WSCPUMonitor::get_cpu_usage(unsigned& busy, unsigned& idle)
|
|||
auto all_processes = CProcessStatisticsReader::get_all();
|
||||
|
||||
for (auto& it : all_processes) {
|
||||
if (it.value.pid == 0)
|
||||
idle += it.value.times_scheduled;
|
||||
else
|
||||
busy += it.value.times_scheduled;
|
||||
for (auto& jt : it.value.threads) {
|
||||
if (it.value.pid == 0)
|
||||
idle += jt.times_scheduled;
|
||||
else
|
||||
busy += jt.times_scheduled;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -28,9 +28,9 @@ int main(int argc, char** argv)
|
|||
proc.pgp,
|
||||
proc.sid,
|
||||
proc.uid,
|
||||
proc.state.characters(),
|
||||
proc.threads.first().state.characters(),
|
||||
proc.ppid,
|
||||
proc.times_scheduled,
|
||||
proc.threads.first().times_scheduled,
|
||||
proc.nfds,
|
||||
tty.characters(),
|
||||
proc.name.characters());
|
||||
|
|
125
Userland/top.cpp
125
Userland/top.cpp
|
@ -1,9 +1,9 @@
|
|||
#include <AK/String.h>
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/JsonArray.h>
|
||||
#include <AK/JsonObject.h>
|
||||
#include <AK/JsonValue.h>
|
||||
#include <AK/QuickSort.h>
|
||||
#include <AK/String.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibCore/CProcessStatisticsReader.h>
|
||||
#include <fcntl.h>
|
||||
|
@ -11,15 +11,55 @@
|
|||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
struct ProcessData {
|
||||
CProcessStatistics stats;
|
||||
struct ThreadData {
|
||||
int tid;
|
||||
pid_t pid;
|
||||
unsigned pgid;
|
||||
unsigned pgp;
|
||||
unsigned sid;
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
pid_t ppid;
|
||||
unsigned nfds;
|
||||
String name;
|
||||
String tty;
|
||||
size_t amount_virtual;
|
||||
size_t amount_resident;
|
||||
size_t amount_shared;
|
||||
unsigned syscall_count;
|
||||
unsigned inode_faults;
|
||||
unsigned zero_faults;
|
||||
unsigned cow_faults;
|
||||
int icon_id;
|
||||
unsigned times_scheduled;
|
||||
|
||||
unsigned times_scheduled_since_prev { 0 };
|
||||
unsigned cpu_percent { 0 };
|
||||
unsigned cpu_percent_decimal { 0 };
|
||||
|
||||
String priority;
|
||||
String username;
|
||||
String state;
|
||||
};
|
||||
|
||||
struct PidAndTid {
|
||||
bool operator==(const PidAndTid& other) const
|
||||
{
|
||||
return pid == other.pid && tid == other.tid;
|
||||
}
|
||||
pid_t pid;
|
||||
int tid;
|
||||
};
|
||||
|
||||
namespace AK {
|
||||
template<>
|
||||
struct Traits<PidAndTid> : public GenericTraits<PidAndTid> {
|
||||
static unsigned hash(const PidAndTid& value) { return pair_int_hash(value.pid, value.tid); }
|
||||
};
|
||||
}
|
||||
|
||||
struct Snapshot {
|
||||
HashMap<unsigned, ProcessData> map;
|
||||
HashMap<PidAndTid, ThreadData> map;
|
||||
u32 sum_times_scheduled { 0 };
|
||||
};
|
||||
|
||||
|
@ -31,10 +71,35 @@ static Snapshot get_snapshot()
|
|||
|
||||
for (auto& it : all_processes) {
|
||||
auto& stats = it.value;
|
||||
snapshot.sum_times_scheduled += stats.times_scheduled;
|
||||
ProcessData process_data;
|
||||
process_data.stats = stats;
|
||||
snapshot.map.set(stats.pid, move(process_data));
|
||||
for (auto& thread : stats.threads) {
|
||||
snapshot.sum_times_scheduled += thread.times_scheduled;
|
||||
ThreadData thread_data;
|
||||
thread_data.tid = thread.tid;
|
||||
thread_data.pid = stats.pid;
|
||||
thread_data.pgid = stats.pgid;
|
||||
thread_data.pgp = stats.pgp;
|
||||
thread_data.sid = stats.sid;
|
||||
thread_data.uid = stats.uid;
|
||||
thread_data.gid = stats.gid;
|
||||
thread_data.ppid = stats.ppid;
|
||||
thread_data.nfds = stats.nfds;
|
||||
thread_data.name = stats.name;
|
||||
thread_data.tty = stats.tty;
|
||||
thread_data.amount_virtual = stats.amount_virtual;
|
||||
thread_data.amount_resident = stats.amount_resident;
|
||||
thread_data.amount_shared = stats.amount_shared;
|
||||
thread_data.syscall_count = stats.syscall_count;
|
||||
thread_data.inode_faults = stats.inode_faults;
|
||||
thread_data.zero_faults = stats.zero_faults;
|
||||
thread_data.cow_faults = stats.cow_faults;
|
||||
thread_data.icon_id = stats.icon_id;
|
||||
thread_data.times_scheduled = thread.times_scheduled;
|
||||
thread_data.priority = thread.priority;
|
||||
thread_data.state = thread.state;
|
||||
thread_data.username = stats.username;
|
||||
|
||||
snapshot.map.set({ stats.pid, thread.tid }, move(thread_data));
|
||||
}
|
||||
}
|
||||
|
||||
return snapshot;
|
||||
|
@ -42,7 +107,7 @@ static Snapshot get_snapshot()
|
|||
|
||||
int main(int, char**)
|
||||
{
|
||||
Vector<ProcessData*> processes;
|
||||
Vector<ThreadData*> threads;
|
||||
auto prev = get_snapshot();
|
||||
usleep(10000);
|
||||
for (;;) {
|
||||
|
@ -50,8 +115,9 @@ int main(int, char**)
|
|||
auto sum_diff = current.sum_times_scheduled - prev.sum_times_scheduled;
|
||||
|
||||
printf("\033[3J\033[H\033[2J");
|
||||
printf("\033[47;30m%6s %3s %-8s %-8s %6s %6s %4s %s\033[K\033[0m\n",
|
||||
printf("\033[47;30m%6s %3s %3s %-8s %-10s %6s %6s %4s %s\033[K\033[0m\n",
|
||||
"PID",
|
||||
"TID",
|
||||
"PRI",
|
||||
"USER",
|
||||
"STATE",
|
||||
|
@ -60,38 +126,39 @@ int main(int, char**)
|
|||
"%CPU",
|
||||
"NAME");
|
||||
for (auto& it : current.map) {
|
||||
pid_t pid = it.key;
|
||||
if (pid == 0)
|
||||
auto pid_and_tid = it.key;
|
||||
if (pid_and_tid.pid == 0)
|
||||
continue;
|
||||
u32 times_scheduled_now = it.value.stats.times_scheduled;
|
||||
auto jt = prev.map.find(pid);
|
||||
u32 times_scheduled_now = it.value.times_scheduled;
|
||||
auto jt = prev.map.find(pid_and_tid);
|
||||
if (jt == prev.map.end())
|
||||
continue;
|
||||
u32 times_scheduled_before = (*jt).value.stats.times_scheduled;
|
||||
u32 times_scheduled_before = (*jt).value.times_scheduled;
|
||||
u32 times_scheduled_diff = times_scheduled_now - times_scheduled_before;
|
||||
it.value.times_scheduled_since_prev = times_scheduled_diff;
|
||||
it.value.cpu_percent = ((times_scheduled_diff * 100) / sum_diff);
|
||||
it.value.cpu_percent_decimal = (((times_scheduled_diff * 1000) / sum_diff) % 10);
|
||||
processes.append(&it.value);
|
||||
threads.append(&it.value);
|
||||
}
|
||||
|
||||
quick_sort(processes.begin(), processes.end(), [](auto* p1, auto* p2) {
|
||||
quick_sort(threads.begin(), threads.end(), [](auto* p1, auto* p2) {
|
||||
return p2->times_scheduled_since_prev < p1->times_scheduled_since_prev;
|
||||
});
|
||||
|
||||
for (auto* process : processes) {
|
||||
printf("%6d %c %-8s %-10s %6zu %6zu %2u.%1u %s\n",
|
||||
process->stats.pid,
|
||||
process->stats.priority[0],
|
||||
process->stats.username.characters(),
|
||||
process->stats.state.characters(),
|
||||
process->stats.amount_virtual / 1024,
|
||||
process->stats.amount_resident / 1024,
|
||||
process->cpu_percent,
|
||||
process->cpu_percent_decimal,
|
||||
process->stats.name.characters());
|
||||
for (auto* thread : threads) {
|
||||
printf("%6d %3d %c %-8s %-10s %6zu %6zu %2u.%1u %s\n",
|
||||
thread->pid,
|
||||
thread->tid,
|
||||
thread->priority[0],
|
||||
thread->username.characters(),
|
||||
thread->state.characters(),
|
||||
thread->amount_virtual / 1024,
|
||||
thread->amount_resident / 1024,
|
||||
thread->cpu_percent,
|
||||
thread->cpu_percent_decimal,
|
||||
thread->name.characters());
|
||||
}
|
||||
processes.clear_with_capacity();
|
||||
threads.clear_with_capacity();
|
||||
prev = move(current);
|
||||
sleep(1);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue