فهرست منبع

Kernel: Change the format of /proc/all to JSON.

Update ProcessManager, top and WSCPUMonitor to handle the new format.

Since the kernel is not allowed to use floating-point math, we now compile
the JSON classes in AK without JsonValue::Type::Double support.
To accomodate large unsigned ints, I added a JsonValue::Type::UnsignedInt.
Andreas Kling 6 سال پیش
والد
کامیت
2bd8118843
9فایلهای تغییر یافته به همراه127 افزوده شده و 103 حذف شده
  1. 3 1
      AK/JsonParser.cpp
  2. 14 7
      AK/JsonValue.cpp
  3. 28 1
      AK/JsonValue.h
  4. 1 1
      AK/Types.h
  5. 17 25
      Applications/ProcessManager/ProcessModel.cpp
  6. 27 23
      Kernel/FileSystem/ProcFS.cpp
  7. 4 0
      Kernel/Makefile
  8. 10 15
      Servers/WindowServer/WSCPUMonitor.cpp
  9. 23 30
      Userland/top.cpp

+ 3 - 1
AK/JsonParser.cpp

@@ -146,7 +146,9 @@ JsonValue JsonParser::parse_number()
 {
     auto number_string = extract_while([](char ch) { return ch == '-' || (ch >= '0' && ch <= '9'); });
     bool ok;
-    auto value = JsonValue(number_string.to_int(ok));
+    auto value = JsonValue(number_string.to_uint(ok));
+    if (!ok)
+        value = JsonValue(number_string.to_int(ok));
     ASSERT(ok);
     return value;
 }

+ 14 - 7
AK/JsonValue.cpp

@@ -68,15 +68,15 @@ JsonValue::JsonValue(int value)
     m_value.as_int = value;
 }
 
+JsonValue::JsonValue(long unsigned value)
+    : JsonValue((unsigned)value)
+{
+}
+
 JsonValue::JsonValue(unsigned value)
+    : m_type(Type::UnsignedInt)
 {
-    if (value > INT32_MAX) {
-        m_type = Type::Double;
-        m_value.as_double = value;
-    } else {
-        m_type = Type::Int;
-        m_value.as_int = (int)value;
-    }
+    m_value.as_uint = value;
 }
 
 JsonValue::JsonValue(const char* cstring)
@@ -84,11 +84,13 @@ JsonValue::JsonValue(const char* cstring)
 {
 }
 
+#ifndef KERNEL
 JsonValue::JsonValue(double value)
     : m_type(Type::Double)
 {
     m_value.as_double = value;
 }
+#endif
 
 JsonValue::JsonValue(bool value)
     : m_type(Type::Bool)
@@ -153,12 +155,17 @@ void JsonValue::serialize(StringBuilder& builder) const
     case Type::Bool:
         builder.append(m_value.as_bool ? "true" : "false");
         break;
+#ifndef KERNEL
     case Type::Double:
         builder.appendf("%g", m_value.as_double);
         break;
+#endif
     case Type::Int:
         builder.appendf("%d", m_value.as_int);
         break;
+    case Type::UnsignedInt:
+        builder.appendf("%u", m_value.as_uint);
+        break;
     case Type::Undefined:
         builder.append("undefined");
         break;

+ 28 - 1
AK/JsonValue.h

@@ -14,7 +14,10 @@ public:
         Undefined,
         Null,
         Int,
+        UnsignedInt,
+#ifndef KERNEL
         Double,
+#endif
         Bool,
         String,
         Array,
@@ -34,7 +37,10 @@ public:
 
     JsonValue(int);
     JsonValue(unsigned);
+    JsonValue(long unsigned);
+#ifndef KERNEL
     JsonValue(double);
+#endif
     JsonValue(bool);
     JsonValue(const char*);
     JsonValue(const String&);
@@ -75,18 +81,36 @@ public:
     bool is_undefined() const { return m_type == Type::Undefined; }
     bool is_string() const { return m_type == Type::String; }
     bool is_int() const { return m_type == Type::Int; }
+    bool is_uint() const { return m_type == Type::UnsignedInt; }
+#ifndef KERNEL
     bool is_double() const { return m_type == Type::Double; }
+#endif
     bool is_array() const { return m_type == Type::Array; }
     bool is_object() const { return m_type == Type::Object; }
-    bool is_number() const { return m_type == Type::Int || m_type == Type::Double; }
+    bool is_number() const
+    {
+        if (m_type == Type::Int || m_type == Type::UnsignedInt)
+            return true;
+#ifdef KERNEL
+        return false;
+#else
+        return m_type == Type::Double;
+#endif
+    }
 
     dword to_dword(dword default_value = 0) const
     {
         if (!is_number())
             return default_value;
+#ifdef KERNEL
+        return (dword)m_value.as_int;
+#else
         if (type() == Type::Int)
             return (dword)m_value.as_int;
+        if (type() == Type::UnsignedInt)
+            return m_value.as_uint;
         return (dword)m_value.as_double;
+#endif
     }
 
 private:
@@ -99,8 +123,11 @@ private:
         StringImpl* as_string { nullptr };
         JsonArray* as_array;
         JsonObject* as_object;
+#ifndef KERNEL
         double as_double;
+#endif
         int as_int;
+        unsigned int as_uint;
         bool as_bool;
     } m_value;
 };

+ 1 - 1
AK/Types.h

@@ -13,7 +13,7 @@ typedef signed short signed_word;
 typedef signed int signed_dword;
 typedef signed long long int signed_qword;
 
-typedef decltype(sizeof(void*)) size_t;
+typedef __SIZE_TYPE__ size_t;
 typedef signed_dword ssize_t;
 
 static_assert(sizeof(size_t) == sizeof(dword));

+ 17 - 25
Applications/ProcessManager/ProcessModel.cpp

@@ -1,5 +1,8 @@
 #include "ProcessModel.h"
 #include "GraphWidget.h"
+#include <AK/JsonArray.h>
+#include <AK/JsonObject.h>
+#include <AK/JsonValue.h>
 #include <LibCore/CFile.h>
 #include <fcntl.h>
 #include <pwd.h>
@@ -191,24 +194,16 @@ void ProcessModel::update()
 
     HashTable<pid_t> live_pids;
     unsigned sum_nsched = 0;
-    for (;;) {
-        auto line = m_proc_all.read_line(1024);
-        if (line.is_empty())
-            break;
-        auto chomped = String((const char*)line.pointer(), line.size() - 1, Chomp);
-        auto parts = chomped.split_view(',');
-        if (parts.size() < 18)
-            break;
-        bool ok;
-        pid_t pid = parts[0].to_uint(ok);
-        ASSERT(ok);
-        unsigned nsched = parts[1].to_uint(ok);
-        ASSERT(ok);
+    auto file_contents = m_proc_all.read_all();
+    auto json = JsonValue::from_string({ file_contents.data(), file_contents.size() });
+    json.as_array().for_each([&](auto& value) {
+        const JsonObject& process_object = value.as_object();
+        pid_t pid = process_object.get("pid").to_dword();
+        unsigned nsched = process_object.get("times_scheduled").to_dword();
         ProcessState state;
         state.pid = pid;
         state.nsched = nsched;
-        unsigned uid = parts[5].to_uint(ok);
-        ASSERT(ok);
+        unsigned uid = process_object.get("uid").to_dword();
         {
             auto it = m_usernames.find((uid_t)uid);
             if (it != m_usernames.end())
@@ -216,15 +211,12 @@ void ProcessModel::update()
             else
                 state.user = String::format("%u", uid);
         }
-        state.priority = parts[16];
-        state.syscalls = parts[17].to_uint(ok);
-        ASSERT(ok);
-        state.state = parts[7];
-        state.name = parts[11];
-        state.virtual_size = parts[12].to_uint(ok);
-        ASSERT(ok);
-        state.physical_size = parts[13].to_uint(ok);
-        ASSERT(ok);
+        state.priority = process_object.get("priority").to_string();
+        state.syscalls = process_object.get("syscall_count").to_dword();
+        state.state = process_object.get("state").to_string();
+        state.name = process_object.get("name").to_string();
+        state.virtual_size = process_object.get("amount_virtual").to_dword();
+        state.physical_size = process_object.get("amount_resident").to_dword();
         sum_nsched += nsched;
         {
             auto it = m_processes.find(pid);
@@ -237,7 +229,7 @@ void ProcessModel::update()
         (*it).value->current_state = state;
 
         live_pids.set(pid);
-    }
+    });
 
     m_pids.clear();
     float total_cpu_percent = 0;

+ 27 - 23
Kernel/FileSystem/ProcFS.cpp

@@ -4,6 +4,9 @@
 #include "Process.h"
 #include "Scheduler.h"
 #include "StdLib.h"
+#include <AK/JsonArray.h>
+#include <AK/JsonObject.h>
+#include <AK/JsonValue.h>
 #include <AK/StringBuilder.h>
 #include <Kernel/Arch/i386/CPU.h>
 #include <Kernel/FileSystem/Custody.h>
@@ -581,32 +584,33 @@ ByteBuffer procfs$all(InodeIdentifier)
 {
     InterruptDisabler disabler;
     auto processes = Process::all_processes();
+    JsonArray array;
     StringBuilder builder(processes.size() * 80);
-    auto build_process_line = [&builder](Process* process) {
-        builder.appendf("%u,%u,%u,%u,%u,%u,%u,%s,%u,%u,%s,%s,%u,%u,%u,%u,%s,%u\n",
-            process->pid(),
-            process->main_thread().times_scheduled(), // FIXME(Thread): Bill all scheds to the process
-            process->tty() ? process->tty()->pgid() : 0,
-            process->pgid(),
-            process->sid(),
-            process->uid(),
-            process->gid(),
-            to_string(process->state()),
-            process->ppid(),
-            process->number_of_open_file_descriptors(),
-            process->tty() ? process->tty()->tty_name().characters() : "notty",
-            process->name().characters(),
-            process->amount_virtual(),
-            process->amount_resident(),
-            process->amount_shared(),
-            process->main_thread().ticks(), // FIXME(Thread): Bill all ticks to the process
-            to_string(process->priority()),
-            process->syscall_count());
+    auto build_process = [&](const Process& process) {
+        JsonObject process_object;
+        process_object.set("pid", process.pid());
+        process_object.set("times_scheduled", process.main_thread().times_scheduled());
+        process_object.set("pgid", process.tty() ? process.tty()->pgid() : 0);
+        process_object.set("sid", process.sid());
+        process_object.set("uid", process.uid());
+        process_object.set("gid", process.gid());
+        process_object.set("state", to_string(process.state()));
+        process_object.set("ppid", process.ppid());
+        process_object.set("nfds", process.number_of_open_file_descriptors());
+        process_object.set("name", process.name());
+        process_object.set("tty", process.tty() ? process.tty()->tty_name() : "notty");
+        process_object.set("amount_virtual", process.amount_virtual());
+        process_object.set("amount_resident", process.amount_resident());
+        process_object.set("amount_shared", process.amount_shared());
+        process_object.set("ticks", process.main_thread().ticks());
+        process_object.set("priority", to_string(process.priority()));
+        process_object.set("syscall_count", process.syscall_count());
+        array.append(process_object);
     };
-    build_process_line(Scheduler::colonel());
+    build_process(*Scheduler::colonel());
     for (auto* process : processes)
-        build_process_line(process);
-    return builder.to_byte_buffer();
+        build_process(*process);
+    return array.serialized().to_byte_buffer();
 }
 
 ByteBuffer procfs$inodes(InodeIdentifier)

+ 4 - 0
Kernel/Makefile

@@ -84,6 +84,10 @@ AK_OBJS = \
     ../AK/StringView.o \
     ../AK/FileSystemPath.o \
     ../AK/StdLibExtras.o \
+    ../AK/JsonObject.o \
+    ../AK/JsonValue.o \
+    ../AK/JsonArray.o \
+    ../AK/JsonParser.o \
     ../AK/ELF/ELFImage.o \
     ../AK/ELF/ELFLoader.o
 

+ 10 - 15
Servers/WindowServer/WSCPUMonitor.cpp

@@ -1,3 +1,6 @@
+#include <AK/JsonArray.h>
+#include <AK/JsonObject.h>
+#include <AK/JsonValue.h>
 #include <WindowServer/WSCPUMonitor.h>
 #include <WindowServer/WSEventLoop.h>
 #include <WindowServer/WSWindowManager.h>
@@ -37,25 +40,17 @@ void WSCPUMonitor::get_cpu_usage(unsigned& busy, unsigned& idle)
     idle = 0;
 
     m_proc_all.seek(0);
-    for (;;) {
-        auto line = m_proc_all.read_line(BUFSIZ);
-        if (line.is_null())
-            break;
-        auto chomped = String((const char*)line.pointer(), line.size() - 1, Chomp);
-        auto parts = chomped.split_view(',');
-        if (parts.size() < 18)
-            break;
-        bool ok;
-        pid_t pid = parts[0].to_uint(ok);
-        ASSERT(ok);
-        unsigned nsched = parts[1].to_uint(ok);
-        ASSERT(ok);
-
+    auto file_contents = m_proc_all.read_all();
+    auto json = JsonValue::from_string({ file_contents.data(), file_contents.size() });
+    json.as_array().for_each([&](auto& value) {
+        const JsonObject& process_object = value.as_object();
+        pid_t pid = process_object.get("pid").to_dword();
+        unsigned nsched = process_object.get("times_scheduled").to_dword();
         if (pid == 0)
             idle += nsched;
         else
             busy += nsched;
-    }
+    });
 }
 
 void WSCPUMonitor::paint(Painter& painter, const Rect& rect)

+ 23 - 30
Userland/top.cpp

@@ -1,7 +1,11 @@
 #include <AK/AKString.h>
 #include <AK/HashMap.h>
+#include <AK/JsonArray.h>
+#include <AK/JsonObject.h>
+#include <AK/JsonValue.h>
 #include <AK/QuickSort.h>
 #include <AK/Vector.h>
+#include <LibCore/CFile.h>
 #include <fcntl.h>
 #include <pwd.h>
 #include <stdio.h>
@@ -31,44 +35,33 @@ struct Snapshot {
 
 static Snapshot get_snapshot()
 {
-    Snapshot snapshot;
-
-    FILE* fp = fopen("/proc/all", "r");
-    if (!fp) {
-        perror("failed to open /proc/all");
+    CFile file("/proc/all");
+    if (!file.open(CIODevice::ReadOnly)) {
+        fprintf(stderr, "Failed to open /proc/all: %s\n", file.error_string());
         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() < 17)
-            break;
-        bool ok;
-        pid_t pid = parts[0].to_uint(ok);
-        ASSERT(ok);
-        unsigned nsched = parts[1].to_uint(ok);
-        ASSERT(ok);
+
+    Snapshot snapshot;
+
+    auto file_contents = file.read_all();
+    auto json = JsonValue::from_string({ file_contents.data(), file_contents.size() });
+    json.as_array().for_each([&](auto& value) {
+        const JsonObject& process_object = value.as_object();
+        pid_t pid = process_object.get("pid").to_dword();
+        unsigned nsched = process_object.get("times_scheduled").to_dword();
         snapshot.sum_nsched += nsched;
         Process process;
         process.pid = pid;
         process.nsched = nsched;
-        unsigned uid = parts[5].to_uint(ok);
-        ASSERT(ok);
+        unsigned uid = process_object.get("uid").to_dword();
         process.user = s_usernames->get(uid);
-        process.priority = parts[16];
-        process.state = parts[7];
-        process.name = parts[11];
-        process.virtual_size = parts[12].to_uint(ok);
-        ASSERT(ok);
-        process.physical_size = parts[13].to_uint(ok);
-        ASSERT(ok);
+        process.priority = process_object.get("priority").to_string();
+        process.state = process_object.get("state").to_string();
+        process.name = process_object.get("name").to_string();
+        process.virtual_size = process_object.get("amount_virtual").to_dword();
+        process.physical_size = process_object.get("amount_resident").to_dword();
         snapshot.map.set(pid, move(process));
-    }
-    int rc = fclose(fp);
-    ASSERT(rc == 0);
+    });
     return snapshot;
 }