Преглед изворни кода

Ladybird+LibWebView: Add ProcessManager to track live processes

This model will be used to add a Processes tab to the inspector.
Andrew Kaster пре 1 година
родитељ
комит
096feaaeb8

+ 3 - 0
Ladybird/AppKit/main.mm

@@ -13,6 +13,7 @@
 #include <LibMain/Main.h>
 #include <LibWebView/CookieJar.h>
 #include <LibWebView/Database.h>
+#include <LibWebView/ProcessManager.h>
 #include <LibWebView/URL.h>
 
 #import <Application/Application.h>
@@ -81,6 +82,8 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
         .wait_for_debugger = debug_web_content ? Ladybird::WaitForDebugger::Yes : Ladybird::WaitForDebugger::No,
     };
 
+    WebView::ProcessManager::initialize();
+
     auto* delegate = [[ApplicationDelegate alloc] init:move(initial_urls)
                                          newTabPageURL:move(new_tab_page_url)
                                          withCookieJar:move(cookie_jar)

+ 9 - 2
Ladybird/HelperProcess.cpp

@@ -6,6 +6,7 @@
 
 #include "HelperProcess.h"
 #include <LibCore/Environment.h>
+#include <LibWebView/ProcessManager.h>
 
 ErrorOr<NonnullRefPtr<WebView::WebContentClient>> launch_web_content_process(
     WebView::ViewImplementation& view,
@@ -24,7 +25,8 @@ ErrorOr<NonnullRefPtr<WebView::WebContentClient>> launch_web_content_process(
     int ui_fd_passing_fd = fd_passing_socket_fds[0];
     int wc_fd_passing_fd = fd_passing_socket_fds[1];
 
-    if (auto child_pid = TRY(Core::System::fork()); child_pid == 0) {
+    auto child_pid = TRY(Core::System::fork());
+    if (child_pid == 0) {
         TRY(Core::System::close(ui_fd_passing_fd));
         TRY(Core::System::close(ui_fd));
 
@@ -94,6 +96,8 @@ ErrorOr<NonnullRefPtr<WebView::WebContentClient>> launch_web_content_process(
         dbgln();
     }
 
+    WebView::ProcessManager::the().add_process(WebView::ProcessType::WebContent, child_pid);
+
     return new_client;
 }
 
@@ -112,7 +116,8 @@ ErrorOr<NonnullRefPtr<Client>> launch_generic_server_process(ReadonlySpan<ByteSt
     int ui_fd_passing_fd = fd_passing_socket_fds[0];
     int server_fd_passing_fd = fd_passing_socket_fds[1];
 
-    if (auto child_pid = TRY(Core::System::fork()); child_pid == 0) {
+    auto child_pid = TRY(Core::System::fork());
+    if (child_pid == 0) {
         TRY(Core::System::close(ui_fd));
         TRY(Core::System::close(ui_fd_passing_fd));
 
@@ -161,6 +166,8 @@ ErrorOr<NonnullRefPtr<Client>> launch_generic_server_process(ReadonlySpan<ByteSt
     auto new_client = TRY(try_make_ref_counted<Client>(move(socket)));
     new_client->set_fd_passing_socket(TRY(Core::LocalSocket::adopt_fd(ui_fd_passing_fd)));
 
+    WebView::ProcessManager::the().add_process(WebView::process_type_from_name(server_name), child_pid);
+
     return new_client;
 }
 

+ 3 - 0
Ladybird/Qt/main.cpp

@@ -18,6 +18,7 @@
 #include <LibMain/Main.h>
 #include <LibWebView/CookieJar.h>
 #include <LibWebView/Database.h>
+#include <LibWebView/ProcessManager.h>
 #include <LibWebView/URL.h>
 #include <QApplication>
 #include <QFileOpenEvent>
@@ -157,6 +158,8 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
         .wait_for_debugger = debug_web_content ? Ladybird::WaitForDebugger::Yes : Ladybird::WaitForDebugger::No,
     };
 
+    WebView::ProcessManager::initialize();
+
     Ladybird::BrowserWindow window(initial_urls, cookie_jar, web_content_options, webdriver_content_ipc_path);
     window.setWindowTitle("Ladybird");
 

+ 1 - 0
Meta/gn/secondary/Userland/Libraries/LibWebView/BUILD.gn

@@ -122,6 +122,7 @@ shared_library("LibWebView") {
     "Database.cpp",
     "History.cpp",
     "InspectorClient.cpp",
+    "ProcessManager.cpp",
     "RequestServerAdapter.cpp",
     "SearchEngine.cpp",
     "SocketPair.cpp",

+ 5 - 0
Userland/Applications/Browser/main.cpp

@@ -26,6 +26,7 @@
 #include <LibWebView/CookieJar.h>
 #include <LibWebView/Database.h>
 #include <LibWebView/OutOfProcessWebView.h>
+#include <LibWebView/ProcessManager.h>
 #include <LibWebView/RequestServerAdapter.h>
 #include <LibWebView/SearchEngine.h>
 #include <LibWebView/URL.h>
@@ -94,6 +95,10 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
         return 1;
     }
 
+    TRY(Core::System::pledge("sigaction stdio recvfd sendfd unix fattr cpath rpath wpath proc exec"));
+
+    WebView::ProcessManager::initialize();
+
     TRY(Core::System::pledge("stdio recvfd sendfd unix fattr cpath rpath wpath proc exec"));
 
     Vector<StringView> specified_urls;

+ 1 - 0
Userland/Libraries/LibWebView/CMakeLists.txt

@@ -6,6 +6,7 @@ set(SOURCES
     Database.cpp
     History.cpp
     InspectorClient.cpp
+    ProcessManager.cpp
     RequestServerAdapter.cpp
     SearchEngine.cpp
     SocketPair.cpp

+ 1 - 0
Userland/Libraries/LibWebView/Forward.h

@@ -15,6 +15,7 @@ class Database;
 class History;
 class InspectorClient;
 class OutOfProcessWebView;
+class ProcessManager;
 class ViewImplementation;
 class WebContentClient;
 

+ 117 - 0
Userland/Libraries/LibWebView/ProcessManager.cpp

@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2024, Andrew Kaster <akaster@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <LibCore/EventLoop.h>
+#include <LibCore/System.h>
+#include <LibWebView/ProcessManager.h>
+
+namespace WebView {
+
+static sig_atomic_t s_received_sigchld = 0;
+
+ProcessType process_type_from_name(StringView name)
+{
+    if (name == "Chrome"sv)
+        return ProcessType::Chrome;
+    if (name == "WebContent"sv)
+        return ProcessType::WebContent;
+    if (name == "WebWorker"sv)
+        return ProcessType::WebWorker;
+    if (name == "SQLServer"sv)
+        return ProcessType::SQLServer;
+    if (name == "RequestServer"sv)
+        return ProcessType::RequestServer;
+    if (name == "ImageDecoder"sv)
+        return ProcessType::ImageDecoder;
+
+    dbgln("Unknown process type: '{}'", name);
+    VERIFY_NOT_REACHED();
+}
+
+StringView process_name_from_type(ProcessType type)
+{
+    switch (type) {
+    case ProcessType::Chrome:
+        return "Chrome"sv;
+    case ProcessType::WebContent:
+        return "WebContent"sv;
+    case ProcessType::WebWorker:
+        return "WebWorker"sv;
+    case ProcessType::SQLServer:
+        return "SQLServer"sv;
+    case ProcessType::RequestServer:
+        return "RequestServer"sv;
+    case ProcessType::ImageDecoder:
+        return "ImageDecoder"sv;
+    }
+    VERIFY_NOT_REACHED();
+}
+
+ProcessManager::ProcessManager()
+{
+}
+
+ProcessManager::~ProcessManager()
+{
+}
+
+ProcessManager& ProcessManager::the()
+{
+    static ProcessManager s_the;
+    return s_the;
+}
+
+void ProcessManager::initialize()
+{
+    // FIXME: Should we change this to call EventLoop::register_signal?
+    //        Note that only EventLoopImplementationUnix has a working register_signal
+
+    struct sigaction action { };
+    action.sa_flags = SA_RESTART;
+    action.sa_sigaction = [](int, auto*, auto) {
+        s_received_sigchld = 1;
+    };
+
+    MUST(Core::System::sigaction(SIGCHLD, &action, nullptr));
+
+    the().add_process(WebView::ProcessType::Chrome, getpid());
+}
+
+void ProcessManager::add_process(ProcessType type, pid_t pid)
+{
+    dbgln("ProcessManager::add_process({}, {})", process_name_from_type(type), pid);
+    m_processes.append({ type, pid, 0, 0 });
+}
+
+void ProcessManager::remove_process(pid_t pid)
+{
+    m_processes.remove_first_matching([&](auto& info) {
+        if (info.pid == pid) {
+            dbgln("ProcessManager: Remove process {} ({})", process_name_from_type(info.type), pid);
+            return true;
+        }
+        return false;
+    });
+}
+
+void ProcessManager::update_all_processes()
+{
+    if (s_received_sigchld) {
+        s_received_sigchld = 0;
+        auto result = Core::System::waitpid(-1, WNOHANG);
+        while (!result.is_error() && result.value().pid > 0) {
+            auto& [pid, status] = result.value();
+            if (WIFEXITED(status) || WIFSIGNALED(status)) {
+                remove_process(pid);
+            }
+            result = Core::System::waitpid(-1, WNOHANG);
+        }
+    }
+
+    // FIXME: Actually gather stats in a platform-specific way
+}
+
+}

+ 53 - 0
Userland/Libraries/LibWebView/ProcessManager.h

@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2024, Andrew Kaster <akaster@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <AK/Types.h>
+#include <AK/Vector.h>
+#include <LibCore/EventReceiver.h>
+#include <LibWebView/Forward.h>
+
+#pragma once
+
+namespace WebView {
+
+enum class ProcessType {
+    Chrome,
+    WebContent,
+    WebWorker,
+    SQLServer,
+    RequestServer,
+    ImageDecoder,
+};
+
+struct ProcessInfo {
+    ProcessType type;
+    pid_t pid;
+    u64 memory_usage_kib = 0;
+    float cpu_percent = 0.0f;
+};
+
+ProcessType process_type_from_name(StringView);
+StringView process_name_from_type(ProcessType type);
+
+class ProcessManager {
+public:
+    static ProcessManager& the();
+    static void initialize();
+
+    void add_process(WebView::ProcessType, pid_t);
+    void remove_process(pid_t);
+
+    void update_all_processes();
+    Vector<ProcessInfo> processes() const { return m_processes; }
+
+private:
+    ProcessManager();
+    ~ProcessManager();
+
+    Vector<ProcessInfo> m_processes;
+};
+
+}