| 
									
										
										
										
											2024-03-25 18:29:14 -06:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Copyright (c) 2024, Andrew Kaster <akaster@serenityos.org> | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * SPDX-License-Identifier: BSD-2-Clause | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-03 05:05:20 -06:00
										 |  |  | #include <AK/NumberFormat.h>
 | 
					
						
							| 
									
										
										
										
											2024-03-26 11:38:12 -06:00
										 |  |  | #include <AK/String.h>
 | 
					
						
							| 
									
										
										
										
											2024-03-25 18:29:14 -06: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()); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void ProcessManager::add_process(ProcessType type, pid_t pid) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2024-04-04 14:10:08 -06:00
										 |  |  |     Threading::MutexLocker locker { m_lock }; | 
					
						
							| 
									
										
										
										
											2024-03-25 18:29:14 -06:00
										 |  |  |     dbgln("ProcessManager::add_process({}, {})", process_name_from_type(type), pid); | 
					
						
							| 
									
										
										
										
											2024-04-04 14:10:08 -06: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 18:29:14 -06:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2024-04-04 14:10:08 -06:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2024-03-25 18:29:14 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | void ProcessManager::remove_process(pid_t pid) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2024-04-04 14:10:08 -06:00
										 |  |  |     Threading::MutexLocker locker { m_lock }; | 
					
						
							| 
									
										
										
										
											2024-04-03 05:05:20 -06:00
										 |  |  |     m_statistics.processes.remove_first_matching([&](auto& info) { | 
					
						
							| 
									
										
										
										
											2024-03-25 18:29:14 -06: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 14:10:08 -06:00
										 |  |  |     Threading::MutexLocker locker { m_lock }; | 
					
						
							| 
									
										
										
										
											2024-04-03 05:05:20 -06:00
										 |  |  |     (void)update_process_statistics(m_statistics); | 
					
						
							| 
									
										
										
										
											2024-03-25 18:29:14 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-26 11:38:12 -06:00
										 |  |  | String ProcessManager::generate_html() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2024-04-04 14:10:08 -06:00
										 |  |  |     Threading::MutexLocker locker { m_lock }; | 
					
						
							| 
									
										
										
										
											2024-03-26 11:38:12 -06:00
										 |  |  |     StringBuilder builder; | 
					
						
							| 
									
										
										
										
											2024-04-04 14:10:08 -06:00
										 |  |  |     auto const& processes = m_statistics.processes; | 
					
						
							| 
									
										
										
										
											2024-03-26 11:38:12 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     builder.append(R"( | 
					
						
							|  |  |  |         <html> | 
					
						
							|  |  |  |         <head> | 
					
						
							|  |  |  |         <style> | 
					
						
							| 
									
										
										
										
											2024-04-09 13:28:39 -06: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 11:38:12 -06: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 14:10:08 -06:00
										 |  |  |     for (auto const& process : processes) { | 
					
						
							| 
									
										
										
										
											2024-03-26 11:38:12 -06: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 05:05:20 -06:00
										 |  |  |         builder.append(human_readable_size(process.memory_usage_bytes)); | 
					
						
							| 
									
										
										
										
											2024-03-26 11:38:12 -06: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 18:29:14 -06:00
										 |  |  | } |