mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 21:48:13 +00:00
Add a /bin/top program for process table monitoring.
It automagically computes %CPU usage based on the number of times a process has been scheduled between samples. The colonel task is used as idle timer. This is pretty cool. :^)
This commit is contained in:
parent
642e2447c9
commit
c0cffe1134
8 changed files with 156 additions and 4 deletions
|
@ -7,6 +7,7 @@
|
||||||
#include "i386.h"
|
#include "i386.h"
|
||||||
#include "KSyms.h"
|
#include "KSyms.h"
|
||||||
#include "Console.h"
|
#include "Console.h"
|
||||||
|
#include "Scheduler.h"
|
||||||
#include <AK/StringBuilder.h>
|
#include <AK/StringBuilder.h>
|
||||||
#include <LibC/errno_numbers.h>
|
#include <LibC/errno_numbers.h>
|
||||||
|
|
||||||
|
@ -486,9 +487,10 @@ ByteBuffer procfs$all(InodeIdentifier)
|
||||||
InterruptDisabler disabler;
|
InterruptDisabler disabler;
|
||||||
auto processes = Process::all_processes();
|
auto processes = Process::all_processes();
|
||||||
StringBuilder builder;
|
StringBuilder builder;
|
||||||
for (auto* process : processes) {
|
auto build_process_line = [&builder] (Process* process) {
|
||||||
builder.appendf("%u,%u,%u,%u,%u,%u,%s,%u,%u,%u,%s,%s,%u,%u,%u\n",
|
builder.appendf("%u,%u,%u,%u,%u,%u,%u,%s,%u,%u,%s,%s,%u,%u,%u\n",
|
||||||
process->pid(),
|
process->pid(),
|
||||||
|
process->times_scheduled(),
|
||||||
process->tty() ? process->tty()->pgid() : 0,
|
process->tty() ? process->tty()->pgid() : 0,
|
||||||
process->pgid(),
|
process->pgid(),
|
||||||
process->sid(),
|
process->sid(),
|
||||||
|
@ -496,7 +498,6 @@ ByteBuffer procfs$all(InodeIdentifier)
|
||||||
process->gid(),
|
process->gid(),
|
||||||
to_string(process->state()),
|
to_string(process->state()),
|
||||||
process->ppid(),
|
process->ppid(),
|
||||||
process->times_scheduled(),
|
|
||||||
process->number_of_open_file_descriptors(),
|
process->number_of_open_file_descriptors(),
|
||||||
process->tty() ? process->tty()->tty_name().characters() : "notty",
|
process->tty() ? process->tty()->tty_name().characters() : "notty",
|
||||||
process->name().characters(),
|
process->name().characters(),
|
||||||
|
@ -504,7 +505,10 @@ ByteBuffer procfs$all(InodeIdentifier)
|
||||||
process->amount_resident(),
|
process->amount_resident(),
|
||||||
process->amount_shared()
|
process->amount_shared()
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
build_process_line(Scheduler::colonel());
|
||||||
|
for (auto* process : processes)
|
||||||
|
build_process_line(process);
|
||||||
return builder.to_byte_buffer();
|
return builder.to_byte_buffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -303,6 +303,11 @@ void Scheduler::prepare_to_modify_tss(Process& process)
|
||||||
load_task_register(s_redirection.selector);
|
load_task_register(s_redirection.selector);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Process* Scheduler::colonel()
|
||||||
|
{
|
||||||
|
return s_colonel_process;
|
||||||
|
}
|
||||||
|
|
||||||
void Scheduler::initialize()
|
void Scheduler::initialize()
|
||||||
{
|
{
|
||||||
memset(&s_redirection, 0, sizeof(s_redirection));
|
memset(&s_redirection, 0, sizeof(s_redirection));
|
||||||
|
|
|
@ -18,6 +18,7 @@ public:
|
||||||
static bool yield();
|
static bool yield();
|
||||||
static bool context_switch(Process&);
|
static bool context_switch(Process&);
|
||||||
static void prepare_to_modify_tss(Process&);
|
static void prepare_to_modify_tss(Process&);
|
||||||
|
static Process* colonel();
|
||||||
private:
|
private:
|
||||||
static void prepare_for_iret_to_new_process();
|
static void prepare_for_iret_to_new_process();
|
||||||
};
|
};
|
||||||
|
|
|
@ -50,6 +50,7 @@ cp -v ../FontEditor/FontEditor mnt/bin/FontEditor
|
||||||
ln -s FontEditor mnt/bin/ff
|
ln -s FontEditor mnt/bin/ff
|
||||||
cp -v ../Userland/dmesg mnt/bin/dmesg
|
cp -v ../Userland/dmesg mnt/bin/dmesg
|
||||||
cp -v ../Userland/chmod mnt/bin/chmod
|
cp -v ../Userland/chmod mnt/bin/chmod
|
||||||
|
cp -v ../Userland/top mnt/bin/top
|
||||||
sh sync-local.sh
|
sh sync-local.sh
|
||||||
cp -v kernel.map mnt/
|
cp -v kernel.map mnt/
|
||||||
ln -s dir_a mnt/dir_cur
|
ln -s dir_a mnt/dir_cur
|
||||||
|
|
1
LibC/inttypes.h
Normal file
1
LibC/inttypes.h
Normal file
|
@ -0,0 +1 @@
|
||||||
|
#include <stdint.h>
|
1
Userland/.gitignore
vendored
1
Userland/.gitignore
vendored
|
@ -29,4 +29,5 @@ rm
|
||||||
cp
|
cp
|
||||||
rmdir
|
rmdir
|
||||||
dmesg
|
dmesg
|
||||||
|
top
|
||||||
chmod
|
chmod
|
||||||
|
|
|
@ -26,6 +26,7 @@ OBJS = \
|
||||||
rmdir.o \
|
rmdir.o \
|
||||||
dmesg.o \
|
dmesg.o \
|
||||||
chmod.o \
|
chmod.o \
|
||||||
|
top.o \
|
||||||
rm.o
|
rm.o
|
||||||
|
|
||||||
APPS = \
|
APPS = \
|
||||||
|
@ -57,6 +58,7 @@ APPS = \
|
||||||
rmdir \
|
rmdir \
|
||||||
dmesg \
|
dmesg \
|
||||||
chmod \
|
chmod \
|
||||||
|
top \
|
||||||
rm
|
rm
|
||||||
|
|
||||||
ARCH_FLAGS =
|
ARCH_FLAGS =
|
||||||
|
@ -164,6 +166,9 @@ rmdir: rmdir.o
|
||||||
chmod: chmod.o
|
chmod: chmod.o
|
||||||
$(LD) -o $@ $(LDFLAGS) $< ../LibC/LibC.a
|
$(LD) -o $@ $(LDFLAGS) $< ../LibC/LibC.a
|
||||||
|
|
||||||
|
top: top.o
|
||||||
|
$(LD) -o $@ $(LDFLAGS) $< ../LibC/LibC.a
|
||||||
|
|
||||||
.cpp.o:
|
.cpp.o:
|
||||||
@echo "CXX $<"; $(CXX) $(CXXFLAGS) -o $@ -c $<
|
@echo "CXX $<"; $(CXX) $(CXXFLAGS) -o $@ -c $<
|
||||||
|
|
||||||
|
|
134
Userland/top.cpp
Normal file
134
Userland/top.cpp
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <pwd.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <AK/HashMap.h>
|
||||||
|
#include <AK/AKString.h>
|
||||||
|
#include <AK/Vector.h>
|
||||||
|
|
||||||
|
static HashMap<unsigned, String>* s_usernames;
|
||||||
|
|
||||||
|
struct Process {
|
||||||
|
pid_t pid;
|
||||||
|
unsigned nsched;
|
||||||
|
String name;
|
||||||
|
String state;
|
||||||
|
String user;
|
||||||
|
unsigned virt;
|
||||||
|
unsigned res;
|
||||||
|
unsigned nsched_since_prev;
|
||||||
|
unsigned cpu_percent;
|
||||||
|
unsigned cpu_percent_decimal;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Snapshot {
|
||||||
|
HashMap<unsigned, Process> map;
|
||||||
|
dword sum_nsched { 0 };
|
||||||
|
};
|
||||||
|
|
||||||
|
static Snapshot get_snapshot()
|
||||||
|
{
|
||||||
|
Snapshot snapshot;
|
||||||
|
|
||||||
|
FILE* fp = fopen("/proc/all", "r");
|
||||||
|
if (!fp) {
|
||||||
|
perror("failed to open /proc/all");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
for (;;) {
|
||||||
|
char buf[4096];
|
||||||
|
char* ptr = fgets(buf, sizeof(buf), fp);
|
||||||
|
if (!ptr)
|
||||||
|
break;
|
||||||
|
auto parts = String(buf, Chomp).split(',');
|
||||||
|
if (parts.size() < 14)
|
||||||
|
break;
|
||||||
|
bool ok;
|
||||||
|
pid_t pid = parts[0].to_uint(ok);
|
||||||
|
ASSERT(ok);
|
||||||
|
unsigned nsched = parts[1].to_uint(ok);
|
||||||
|
ASSERT(ok);
|
||||||
|
snapshot.sum_nsched += nsched;
|
||||||
|
Process process;
|
||||||
|
process.pid = pid;
|
||||||
|
process.nsched = nsched;
|
||||||
|
unsigned uid = parts[5].to_uint(ok);
|
||||||
|
ASSERT(ok);
|
||||||
|
process.user = s_usernames->get(uid);
|
||||||
|
process.state = parts[7];
|
||||||
|
process.name = parts[11];
|
||||||
|
process.virt = parts[12].to_uint(ok);
|
||||||
|
ASSERT(ok);
|
||||||
|
process.res = parts[13].to_uint(ok);
|
||||||
|
ASSERT(ok);
|
||||||
|
snapshot.map.set(pid, move(process));
|
||||||
|
}
|
||||||
|
int rc = fclose(fp);
|
||||||
|
ASSERT(rc == 0);
|
||||||
|
return snapshot;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int, char**)
|
||||||
|
{
|
||||||
|
s_usernames = new HashMap<unsigned, String>();
|
||||||
|
setpwent();
|
||||||
|
while (auto* passwd = getpwent())
|
||||||
|
s_usernames->set(passwd->pw_uid, passwd->pw_name);
|
||||||
|
endpwent();
|
||||||
|
|
||||||
|
Vector<Process*> processes;
|
||||||
|
auto prev = get_snapshot();
|
||||||
|
for (;;) {
|
||||||
|
auto current = get_snapshot();
|
||||||
|
auto sum_diff = current.sum_nsched - prev.sum_nsched;
|
||||||
|
|
||||||
|
printf("\033[3J\033[H\033[2J");
|
||||||
|
printf("\033[47;30m%6s % 8s %8s %8s %8s %4s %s\033[K\033[0m\n",
|
||||||
|
"PID",
|
||||||
|
"USER",
|
||||||
|
"STATE",
|
||||||
|
"VIRTUAL",
|
||||||
|
"RESIDENT",
|
||||||
|
"%CPU",
|
||||||
|
"NAME");
|
||||||
|
for (auto& it : current.map) {
|
||||||
|
pid_t pid = it.key;
|
||||||
|
if (pid == 0)
|
||||||
|
continue;
|
||||||
|
dword nsched_now = it.value.nsched;
|
||||||
|
auto jt = prev.map.find(pid);
|
||||||
|
if (jt == prev.map.end())
|
||||||
|
continue;
|
||||||
|
dword nsched_before = (*jt).value.nsched;
|
||||||
|
dword nsched_diff = nsched_now - nsched_before;
|
||||||
|
it.value.nsched_since_prev = nsched_diff;
|
||||||
|
it.value.cpu_percent = ((nsched_diff * 100)/ sum_diff);
|
||||||
|
it.value.cpu_percent_decimal = (((nsched_diff * 1000)/ sum_diff) % 10);
|
||||||
|
processes.append(&it.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
qsort(processes.data(), processes.size(), sizeof(Process*), [] (const void* a, const void* b) -> int {
|
||||||
|
auto* p1 = *(const Process* const*)(a);
|
||||||
|
auto* p2 = *(const Process* const*)(b);
|
||||||
|
return p2->nsched_since_prev - p1->nsched_since_prev;
|
||||||
|
});
|
||||||
|
|
||||||
|
for (auto* process : processes) {
|
||||||
|
printf("%6d % 8s %8s %8u %8u %2u.%1u %s\n",
|
||||||
|
process->pid,
|
||||||
|
process->user.characters(),
|
||||||
|
process->state.characters(),
|
||||||
|
process->virt / 1024,
|
||||||
|
process->res / 1024,
|
||||||
|
process->cpu_percent,
|
||||||
|
process->cpu_percent_decimal,
|
||||||
|
process->name.characters()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
processes.clear_with_capacity();
|
||||||
|
prev = move(current);
|
||||||
|
sleep(1);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue