ProcessManager.cpp 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. /*
  2. * Copyright (c) 2024, Andrew Kaster <akaster@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/NumberFormat.h>
  7. #include <AK/String.h>
  8. #include <LibCore/EventLoop.h>
  9. #include <LibCore/System.h>
  10. #include <LibWebView/ProcessManager.h>
  11. namespace WebView {
  12. static sig_atomic_t s_received_sigchld = 0;
  13. ProcessType process_type_from_name(StringView name)
  14. {
  15. if (name == "Chrome"sv)
  16. return ProcessType::Chrome;
  17. if (name == "WebContent"sv)
  18. return ProcessType::WebContent;
  19. if (name == "WebWorker"sv)
  20. return ProcessType::WebWorker;
  21. if (name == "SQLServer"sv)
  22. return ProcessType::SQLServer;
  23. if (name == "RequestServer"sv)
  24. return ProcessType::RequestServer;
  25. if (name == "ImageDecoder"sv)
  26. return ProcessType::ImageDecoder;
  27. dbgln("Unknown process type: '{}'", name);
  28. VERIFY_NOT_REACHED();
  29. }
  30. StringView process_name_from_type(ProcessType type)
  31. {
  32. switch (type) {
  33. case ProcessType::Chrome:
  34. return "Chrome"sv;
  35. case ProcessType::WebContent:
  36. return "WebContent"sv;
  37. case ProcessType::WebWorker:
  38. return "WebWorker"sv;
  39. case ProcessType::SQLServer:
  40. return "SQLServer"sv;
  41. case ProcessType::RequestServer:
  42. return "RequestServer"sv;
  43. case ProcessType::ImageDecoder:
  44. return "ImageDecoder"sv;
  45. }
  46. VERIFY_NOT_REACHED();
  47. }
  48. ProcessManager::ProcessManager()
  49. {
  50. }
  51. ProcessManager::~ProcessManager()
  52. {
  53. }
  54. ProcessManager& ProcessManager::the()
  55. {
  56. static ProcessManager s_the;
  57. return s_the;
  58. }
  59. void ProcessManager::initialize()
  60. {
  61. // FIXME: Should we change this to call EventLoop::register_signal?
  62. // Note that only EventLoopImplementationUnix has a working register_signal
  63. struct sigaction action { };
  64. action.sa_flags = SA_RESTART;
  65. action.sa_sigaction = [](int, auto*, auto) {
  66. s_received_sigchld = 1;
  67. };
  68. MUST(Core::System::sigaction(SIGCHLD, &action, nullptr));
  69. the().add_process(WebView::ProcessType::Chrome, getpid());
  70. #ifdef AK_OS_MACH
  71. auto self_send_port = mach_task_self();
  72. auto res = mach_port_mod_refs(mach_task_self(), self_send_port, MACH_PORT_RIGHT_SEND, +1);
  73. VERIFY(res == KERN_SUCCESS);
  74. the().add_process(getpid(), Core::MachPort::adopt_right(self_send_port, Core::MachPort::PortRight::Send));
  75. #endif
  76. }
  77. ProcessInfo* ProcessManager::find_process(pid_t pid)
  78. {
  79. if (auto existing_process = m_statistics.processes.find_if([&](auto& info) { return info->pid == pid; }); !existing_process.is_end())
  80. return verify_cast<ProcessInfo>(existing_process->ptr());
  81. return nullptr;
  82. }
  83. void ProcessManager::add_process(ProcessType type, pid_t pid)
  84. {
  85. Threading::MutexLocker locker { m_lock };
  86. dbgln("ProcessManager::add_process({}, {})", process_name_from_type(type), pid);
  87. if (auto* existing_process = find_process(pid)) {
  88. existing_process->type = type;
  89. return;
  90. }
  91. m_statistics.processes.append(make<ProcessInfo>(type, pid));
  92. }
  93. #if defined(AK_OS_MACH)
  94. void ProcessManager::add_process(pid_t pid, Core::MachPort&& port)
  95. {
  96. Threading::MutexLocker locker { m_lock };
  97. dbgln("ProcessManager::add_process({}, {:p})", pid, port.port());
  98. if (auto* existing_process = find_process(pid)) {
  99. existing_process->child_task_port = move(port);
  100. return;
  101. }
  102. m_statistics.processes.append(make<ProcessInfo>(pid, move(port)));
  103. }
  104. #endif
  105. void ProcessManager::remove_process(pid_t pid)
  106. {
  107. Threading::MutexLocker locker { m_lock };
  108. m_statistics.processes.remove_first_matching([&](auto const& info) {
  109. if (info->pid == pid) {
  110. auto type = verify_cast<ProcessInfo>(*info).type;
  111. dbgln("ProcessManager: Remove process {} ({})", process_name_from_type(type), pid);
  112. return true;
  113. }
  114. return false;
  115. });
  116. }
  117. void ProcessManager::update_all_processes()
  118. {
  119. if (s_received_sigchld) {
  120. s_received_sigchld = 0;
  121. auto result = Core::System::waitpid(-1, WNOHANG);
  122. while (!result.is_error() && result.value().pid > 0) {
  123. auto& [pid, status] = result.value();
  124. if (WIFEXITED(status) || WIFSIGNALED(status)) {
  125. remove_process(pid);
  126. }
  127. result = Core::System::waitpid(-1, WNOHANG);
  128. }
  129. }
  130. Threading::MutexLocker locker { m_lock };
  131. (void)update_process_statistics(m_statistics);
  132. }
  133. String ProcessManager::generate_html()
  134. {
  135. Threading::MutexLocker locker { m_lock };
  136. StringBuilder builder;
  137. builder.append(R"(
  138. <html>
  139. <head>
  140. <title>Task Manager</title>
  141. <style>
  142. @media (prefers-color-scheme: dark) {
  143. /* FIXME: We should be able to remove the HTML style when "color-scheme" is supported */
  144. html {
  145. background-color: rgb(30, 30, 30);
  146. color: white;
  147. }
  148. tr:nth-child(even) {
  149. background: rgb(57, 57, 57);
  150. }
  151. }
  152. @media (prefers-color-scheme: light) {
  153. tr:nth-child(even) {
  154. background: #f7f7f7;
  155. }
  156. }
  157. table {
  158. width: 100%;
  159. border-collapse: collapse;
  160. }
  161. th {
  162. text-align: left;
  163. border-bottom: 1px solid #aaa;
  164. }
  165. td, th {
  166. padding: 4px;
  167. border: 1px solid #aaa;
  168. }
  169. </style>
  170. </head>
  171. <body>
  172. <table>
  173. <thead>
  174. <tr>
  175. <th>Name</th>
  176. <th>PID</th>
  177. <th>Memory Usage</th>
  178. <th>CPU %</th>
  179. </tr>
  180. </thead>
  181. <tbody>
  182. )"sv);
  183. m_statistics.for_each_process<ProcessInfo>([&](auto const& process) {
  184. builder.append("<tr>"sv);
  185. builder.append("<td>"sv);
  186. builder.append(WebView::process_name_from_type(process.type));
  187. if (process.title.has_value())
  188. builder.appendff(" - {}", escape_html_entities(*process.title));
  189. builder.append("</td>"sv);
  190. builder.append("<td>"sv);
  191. builder.append(MUST(String::number(process.pid)));
  192. builder.append("</td>"sv);
  193. builder.append("<td>"sv);
  194. builder.append(human_readable_size(process.memory_usage_bytes));
  195. builder.append("</td>"sv);
  196. builder.append("<td>"sv);
  197. builder.append(MUST(String::formatted("{:.1f}", process.cpu_percent)));
  198. builder.append("</td>"sv);
  199. builder.append("</tr>"sv);
  200. });
  201. builder.append(R"(
  202. </tbody>
  203. </table>
  204. </body>
  205. </html>
  206. )"sv);
  207. return builder.to_string_without_validation();
  208. }
  209. }