123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230 |
- /*
- * Copyright (c) 2024, Andrew Kaster <akaster@serenityos.org>
- *
- * SPDX-License-Identifier: BSD-2-Clause
- */
- #include <AK/NumberFormat.h>
- #include <AK/String.h>
- #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 == "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::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());
- #ifdef AK_OS_MACH
- auto self_send_port = mach_task_self();
- auto res = mach_port_mod_refs(mach_task_self(), self_send_port, MACH_PORT_RIGHT_SEND, +1);
- VERIFY(res == KERN_SUCCESS);
- the().add_process(getpid(), Core::MachPort::adopt_right(self_send_port, Core::MachPort::PortRight::Send));
- #endif
- }
- ProcessInfo* ProcessManager::find_process(pid_t pid)
- {
- if (auto existing_process = m_statistics.processes.find_if([&](auto& info) { return info->pid == pid; }); !existing_process.is_end())
- return verify_cast<ProcessInfo>(existing_process->ptr());
- return nullptr;
- }
- void ProcessManager::add_process(ProcessType type, pid_t pid)
- {
- Threading::MutexLocker locker { m_lock };
- if (auto* existing_process = find_process(pid)) {
- existing_process->type = type;
- return;
- }
- m_statistics.processes.append(make<ProcessInfo>(type, pid));
- }
- #if defined(AK_OS_MACH)
- void ProcessManager::add_process(pid_t pid, Core::MachPort&& port)
- {
- Threading::MutexLocker locker { m_lock };
- if (auto* existing_process = find_process(pid)) {
- existing_process->child_task_port = move(port);
- return;
- }
- m_statistics.processes.append(make<ProcessInfo>(pid, move(port)));
- }
- #endif
- void ProcessManager::remove_process(pid_t pid)
- {
- Threading::MutexLocker locker { m_lock };
- m_statistics.processes.remove_first_matching([&](auto const& info) {
- if (info->pid == 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);
- }
- }
- Threading::MutexLocker locker { m_lock };
- (void)update_process_statistics(m_statistics);
- }
- String ProcessManager::generate_html()
- {
- Threading::MutexLocker locker { m_lock };
- StringBuilder builder;
- builder.append(R"(
- <html>
- <head>
- <title>Task Manager</title>
- <style>
- @media (prefers-color-scheme: dark) {
- /* FIXME: We should be able to remove the HTML style when "color-scheme" is supported */
- html {
- background-color: rgb(30, 30, 30);
- color: white;
- }
- tr:nth-child(even) {
- background: rgb(57, 57, 57);
- }
- }
- @media (prefers-color-scheme: light) {
- tr:nth-child(even) {
- background: #f7f7f7;
- }
- }
- table {
- width: 100%;
- border-collapse: collapse;
- }
- th {
- text-align: left;
- border-bottom: 1px solid #aaa;
- }
- td, th {
- padding: 4px;
- border: 1px solid #aaa;
- }
- </style>
- </head>
- <body>
- <table>
- <thead>
- <tr>
- <th>Name</th>
- <th>PID</th>
- <th>Memory Usage</th>
- <th>CPU %</th>
- </tr>
- </thead>
- <tbody>
- )"sv);
- m_statistics.for_each_process<ProcessInfo>([&](auto const& process) {
- builder.append("<tr>"sv);
- builder.append("<td>"sv);
- builder.append(WebView::process_name_from_type(process.type));
- if (process.title.has_value())
- builder.appendff(" - {}", escape_html_entities(*process.title));
- builder.append("</td>"sv);
- builder.append("<td>"sv);
- builder.append(MUST(String::number(process.pid)));
- builder.append("</td>"sv);
- builder.append("<td>"sv);
- builder.append(human_readable_size(process.memory_usage_bytes));
- builder.append("</td>"sv);
- builder.append("<td>"sv);
- builder.append(MUST(String::formatted("{:.1f}", process.cpu_percent)));
- builder.append("</td>"sv);
- builder.append("</tr>"sv);
- });
- builder.append(R"(
- </tbody>
- </table>
- </body>
- </html>
- )"sv);
- return builder.to_string_without_validation();
- }
- }
|