From 096feaaeb8b1dd1c801cb0f97be682bc54361863 Mon Sep 17 00:00:00 2001 From: Andrew Kaster Date: Mon, 25 Mar 2024 18:29:14 -0600 Subject: [PATCH] Ladybird+LibWebView: Add ProcessManager to track live processes This model will be used to add a Processes tab to the inspector. --- Ladybird/AppKit/main.mm | 3 + Ladybird/HelperProcess.cpp | 11 +- Ladybird/Qt/main.cpp | 3 + .../Userland/Libraries/LibWebView/BUILD.gn | 1 + Userland/Applications/Browser/main.cpp | 5 + Userland/Libraries/LibWebView/CMakeLists.txt | 1 + Userland/Libraries/LibWebView/Forward.h | 1 + .../Libraries/LibWebView/ProcessManager.cpp | 117 ++++++++++++++++++ .../Libraries/LibWebView/ProcessManager.h | 53 ++++++++ 9 files changed, 193 insertions(+), 2 deletions(-) create mode 100644 Userland/Libraries/LibWebView/ProcessManager.cpp create mode 100644 Userland/Libraries/LibWebView/ProcessManager.h diff --git a/Ladybird/AppKit/main.mm b/Ladybird/AppKit/main.mm index 0034f56aa5e..4b14524c698 100644 --- a/Ladybird/AppKit/main.mm +++ b/Ladybird/AppKit/main.mm @@ -13,6 +13,7 @@ #include #include #include +#include #include #import @@ -81,6 +82,8 @@ ErrorOr 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) diff --git a/Ladybird/HelperProcess.cpp b/Ladybird/HelperProcess.cpp index a45a51fe2a0..7933fbebbbc 100644 --- a/Ladybird/HelperProcess.cpp +++ b/Ladybird/HelperProcess.cpp @@ -6,6 +6,7 @@ #include "HelperProcess.h" #include +#include ErrorOr> launch_web_content_process( WebView::ViewImplementation& view, @@ -24,7 +25,8 @@ ErrorOr> 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> launch_web_content_process( dbgln(); } + WebView::ProcessManager::the().add_process(WebView::ProcessType::WebContent, child_pid); + return new_client; } @@ -112,7 +116,8 @@ ErrorOr> launch_generic_server_process(ReadonlySpan> launch_generic_server_process(ReadonlySpan(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; } diff --git a/Ladybird/Qt/main.cpp b/Ladybird/Qt/main.cpp index b02eb7db42c..c78230dbd0d 100644 --- a/Ladybird/Qt/main.cpp +++ b/Ladybird/Qt/main.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -157,6 +158,8 @@ ErrorOr 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"); diff --git a/Meta/gn/secondary/Userland/Libraries/LibWebView/BUILD.gn b/Meta/gn/secondary/Userland/Libraries/LibWebView/BUILD.gn index a23901b99c6..f680473f7c9 100644 --- a/Meta/gn/secondary/Userland/Libraries/LibWebView/BUILD.gn +++ b/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", diff --git a/Userland/Applications/Browser/main.cpp b/Userland/Applications/Browser/main.cpp index 0ea8e780b45..9e7cf196270 100644 --- a/Userland/Applications/Browser/main.cpp +++ b/Userland/Applications/Browser/main.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -94,6 +95,10 @@ ErrorOr 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 specified_urls; diff --git a/Userland/Libraries/LibWebView/CMakeLists.txt b/Userland/Libraries/LibWebView/CMakeLists.txt index c5cee84b911..8cbaa4bea49 100644 --- a/Userland/Libraries/LibWebView/CMakeLists.txt +++ b/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 diff --git a/Userland/Libraries/LibWebView/Forward.h b/Userland/Libraries/LibWebView/Forward.h index 8334c88b15d..83415c91a46 100644 --- a/Userland/Libraries/LibWebView/Forward.h +++ b/Userland/Libraries/LibWebView/Forward.h @@ -15,6 +15,7 @@ class Database; class History; class InspectorClient; class OutOfProcessWebView; +class ProcessManager; class ViewImplementation; class WebContentClient; diff --git a/Userland/Libraries/LibWebView/ProcessManager.cpp b/Userland/Libraries/LibWebView/ProcessManager.cpp new file mode 100644 index 00000000000..12e0a8b7d6f --- /dev/null +++ b/Userland/Libraries/LibWebView/ProcessManager.cpp @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2024, Andrew Kaster + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include + +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 +} + +} diff --git a/Userland/Libraries/LibWebView/ProcessManager.h b/Userland/Libraries/LibWebView/ProcessManager.h new file mode 100644 index 00000000000..266a41175cc --- /dev/null +++ b/Userland/Libraries/LibWebView/ProcessManager.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2024, Andrew Kaster + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include + +#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 processes() const { return m_processes; } + +private: + ProcessManager(); + ~ProcessManager(); + + Vector m_processes; +}; + +}