diff --git a/Kernel/ProcFS.cpp b/Kernel/ProcFS.cpp index 86d1eb501b3..f80a2be5434 100644 --- a/Kernel/ProcFS.cpp +++ b/Kernel/ProcFS.cpp @@ -7,6 +7,7 @@ #include "i386.h" #include "KSyms.h" #include "Console.h" +#include "Scheduler.h" #include #include @@ -486,9 +487,10 @@ ByteBuffer procfs$all(InodeIdentifier) InterruptDisabler disabler; auto processes = Process::all_processes(); StringBuilder builder; - for (auto* process : processes) { - builder.appendf("%u,%u,%u,%u,%u,%u,%s,%u,%u,%u,%s,%s,%u,%u,%u\n", + auto build_process_line = [&builder] (Process* process) { + builder.appendf("%u,%u,%u,%u,%u,%u,%u,%s,%u,%u,%s,%s,%u,%u,%u\n", process->pid(), + process->times_scheduled(), process->tty() ? process->tty()->pgid() : 0, process->pgid(), process->sid(), @@ -496,7 +498,6 @@ ByteBuffer procfs$all(InodeIdentifier) process->gid(), to_string(process->state()), process->ppid(), - process->times_scheduled(), process->number_of_open_file_descriptors(), process->tty() ? process->tty()->tty_name().characters() : "notty", process->name().characters(), @@ -504,7 +505,10 @@ ByteBuffer procfs$all(InodeIdentifier) process->amount_resident(), process->amount_shared() ); - } + }; + build_process_line(Scheduler::colonel()); + for (auto* process : processes) + build_process_line(process); return builder.to_byte_buffer(); } diff --git a/Kernel/Scheduler.cpp b/Kernel/Scheduler.cpp index 8cc88c24927..62c01786199 100644 --- a/Kernel/Scheduler.cpp +++ b/Kernel/Scheduler.cpp @@ -303,6 +303,11 @@ void Scheduler::prepare_to_modify_tss(Process& process) load_task_register(s_redirection.selector); } +Process* Scheduler::colonel() +{ + return s_colonel_process; +} + void Scheduler::initialize() { memset(&s_redirection, 0, sizeof(s_redirection)); diff --git a/Kernel/Scheduler.h b/Kernel/Scheduler.h index 813939f5bb8..bd6f3f4ddce 100644 --- a/Kernel/Scheduler.h +++ b/Kernel/Scheduler.h @@ -18,6 +18,7 @@ public: static bool yield(); static bool context_switch(Process&); static void prepare_to_modify_tss(Process&); + static Process* colonel(); private: static void prepare_for_iret_to_new_process(); }; diff --git a/Kernel/sync.sh b/Kernel/sync.sh index bb5a353045b..c71ea67092b 100755 --- a/Kernel/sync.sh +++ b/Kernel/sync.sh @@ -50,6 +50,7 @@ cp -v ../FontEditor/FontEditor mnt/bin/FontEditor ln -s FontEditor mnt/bin/ff cp -v ../Userland/dmesg mnt/bin/dmesg cp -v ../Userland/chmod mnt/bin/chmod +cp -v ../Userland/top mnt/bin/top sh sync-local.sh cp -v kernel.map mnt/ ln -s dir_a mnt/dir_cur diff --git a/LibC/inttypes.h b/LibC/inttypes.h new file mode 100644 index 00000000000..9a6118bd859 --- /dev/null +++ b/LibC/inttypes.h @@ -0,0 +1 @@ +#include diff --git a/Userland/.gitignore b/Userland/.gitignore index d6ac23a0486..bb5127117b5 100644 --- a/Userland/.gitignore +++ b/Userland/.gitignore @@ -29,4 +29,5 @@ rm cp rmdir dmesg +top chmod diff --git a/Userland/Makefile b/Userland/Makefile index 863f658741f..990a8b09b35 100644 --- a/Userland/Makefile +++ b/Userland/Makefile @@ -26,6 +26,7 @@ OBJS = \ rmdir.o \ dmesg.o \ chmod.o \ + top.o \ rm.o APPS = \ @@ -57,6 +58,7 @@ APPS = \ rmdir \ dmesg \ chmod \ + top \ rm ARCH_FLAGS = @@ -164,6 +166,9 @@ rmdir: rmdir.o chmod: chmod.o $(LD) -o $@ $(LDFLAGS) $< ../LibC/LibC.a +top: top.o + $(LD) -o $@ $(LDFLAGS) $< ../LibC/LibC.a + .cpp.o: @echo "CXX $<"; $(CXX) $(CXXFLAGS) -o $@ -c $< diff --git a/Userland/top.cpp b/Userland/top.cpp new file mode 100644 index 00000000000..aab16a1a81a --- /dev/null +++ b/Userland/top.cpp @@ -0,0 +1,134 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +static HashMap* 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 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(); + setpwent(); + while (auto* passwd = getpwent()) + s_usernames->set(passwd->pw_uid, passwd->pw_name); + endpwent(); + + Vector 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; +}