2024-03-25 20:29:14 -04:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2024, Andrew Kaster <akaster@serenityos.org>
|
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
|
|
*/
|
|
|
|
|
2024-04-03 07:05:20 -04:00
|
|
|
#include <AK/NumberFormat.h>
|
2024-03-26 13:38:12 -04:00
|
|
|
#include <AK/String.h>
|
2024-03-25 20:29:14 -04:00
|
|
|
#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());
|
2024-04-04 16:13:14 -04:00
|
|
|
#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
|
2024-03-25 20:29:14 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void ProcessManager::add_process(ProcessType type, pid_t pid)
|
|
|
|
{
|
2024-04-04 16:10:08 -04:00
|
|
|
Threading::MutexLocker locker { m_lock };
|
2024-03-25 20:29:14 -04:00
|
|
|
dbgln("ProcessManager::add_process({}, {})", process_name_from_type(type), pid);
|
2024-04-04 16:10:08 -04:00
|
|
|
if (auto existing_process = m_statistics.processes.find_if([&](auto& info) { return info.pid == pid; }); !existing_process.is_end()) {
|
|
|
|
existing_process->type = type;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
m_statistics.processes.append({ type, pid });
|
|
|
|
}
|
|
|
|
|
|
|
|
#if defined(AK_OS_MACH)
|
|
|
|
void ProcessManager::add_process(pid_t pid, Core::MachPort&& port)
|
|
|
|
{
|
|
|
|
Threading::MutexLocker locker { m_lock };
|
|
|
|
dbgln("ProcessManager::add_process({}, {:p})", pid, port.port());
|
|
|
|
if (auto existing_process = m_statistics.processes.find_if([&](auto& info) { return info.pid == pid; }); !existing_process.is_end()) {
|
|
|
|
existing_process->child_task_port = move(port);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
m_statistics.processes.append({ pid, move(port) });
|
2024-03-25 20:29:14 -04:00
|
|
|
}
|
2024-04-04 16:10:08 -04:00
|
|
|
#endif
|
2024-03-25 20:29:14 -04:00
|
|
|
|
|
|
|
void ProcessManager::remove_process(pid_t pid)
|
|
|
|
{
|
2024-04-04 16:10:08 -04:00
|
|
|
Threading::MutexLocker locker { m_lock };
|
2024-04-03 07:05:20 -04:00
|
|
|
m_statistics.processes.remove_first_matching([&](auto& info) {
|
2024-03-25 20:29:14 -04:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-04 16:10:08 -04:00
|
|
|
Threading::MutexLocker locker { m_lock };
|
2024-04-03 07:05:20 -04:00
|
|
|
(void)update_process_statistics(m_statistics);
|
2024-03-25 20:29:14 -04:00
|
|
|
}
|
|
|
|
|
2024-03-26 13:38:12 -04:00
|
|
|
String ProcessManager::generate_html()
|
|
|
|
{
|
2024-04-04 16:10:08 -04:00
|
|
|
Threading::MutexLocker locker { m_lock };
|
2024-03-26 13:38:12 -04:00
|
|
|
StringBuilder builder;
|
2024-04-04 16:10:08 -04:00
|
|
|
auto const& processes = m_statistics.processes;
|
2024-03-26 13:38:12 -04:00
|
|
|
|
|
|
|
builder.append(R"(
|
|
|
|
<html>
|
|
|
|
<head>
|
|
|
|
<style>
|
2024-04-09 15:28:39 -04:00
|
|
|
@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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-26 13:38:12 -04:00
|
|
|
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>Type</th>
|
|
|
|
<th>PID</th>
|
|
|
|
<th>Memory Usage</th>
|
|
|
|
<th>CPU %</th>
|
|
|
|
</tr>
|
|
|
|
</thead>
|
|
|
|
<tbody>
|
|
|
|
)"sv);
|
|
|
|
|
2024-04-04 16:10:08 -04:00
|
|
|
for (auto const& process : processes) {
|
2024-03-26 13:38:12 -04:00
|
|
|
builder.append("<tr>"sv);
|
|
|
|
builder.append("<td>"sv);
|
|
|
|
builder.append(WebView::process_name_from_type(process.type));
|
|
|
|
builder.append("</td>"sv);
|
|
|
|
builder.append("<td>"sv);
|
|
|
|
builder.append(MUST(String::number(process.pid)));
|
|
|
|
builder.append("</td>"sv);
|
|
|
|
builder.append("<td>"sv);
|
2024-04-03 07:05:20 -04:00
|
|
|
builder.append(human_readable_size(process.memory_usage_bytes));
|
2024-03-26 13:38:12 -04:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2024-03-25 20:29:14 -04:00
|
|
|
}
|