ProcessManager.cpp 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  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. ProcessType process_type_from_name(StringView name)
  13. {
  14. if (name == "Chrome"sv)
  15. return ProcessType::Chrome;
  16. if (name == "WebContent"sv)
  17. return ProcessType::WebContent;
  18. if (name == "WebWorker"sv)
  19. return ProcessType::WebWorker;
  20. if (name == "RequestServer"sv)
  21. return ProcessType::RequestServer;
  22. if (name == "ImageDecoder"sv)
  23. return ProcessType::ImageDecoder;
  24. dbgln("Unknown process type: '{}'", name);
  25. VERIFY_NOT_REACHED();
  26. }
  27. StringView process_name_from_type(ProcessType type)
  28. {
  29. switch (type) {
  30. case ProcessType::Chrome:
  31. return "Chrome"sv;
  32. case ProcessType::WebContent:
  33. return "WebContent"sv;
  34. case ProcessType::WebWorker:
  35. return "WebWorker"sv;
  36. case ProcessType::RequestServer:
  37. return "RequestServer"sv;
  38. case ProcessType::ImageDecoder:
  39. return "ImageDecoder"sv;
  40. }
  41. VERIFY_NOT_REACHED();
  42. }
  43. ProcessManager::ProcessManager()
  44. : on_process_exited([](Process&&) {})
  45. {
  46. m_signal_handle = Core::EventLoop::register_signal(SIGCHLD, [this](int) {
  47. auto result = Core::System::waitpid(-1, WNOHANG);
  48. while (!result.is_error() && result.value().pid > 0) {
  49. auto& [pid, status] = result.value();
  50. if (WIFEXITED(status) || WIFSIGNALED(status)) {
  51. if (auto process = remove_process(pid); process.has_value())
  52. on_process_exited(process.release_value());
  53. }
  54. result = Core::System::waitpid(-1, WNOHANG);
  55. }
  56. });
  57. add_process(Process(WebView::ProcessType::Chrome, nullptr, Core::Process::current()));
  58. #ifdef AK_OS_MACH
  59. auto self_send_port = mach_task_self();
  60. auto res = mach_port_mod_refs(mach_task_self(), self_send_port, MACH_PORT_RIGHT_SEND, +1);
  61. VERIFY(res == KERN_SUCCESS);
  62. set_process_mach_port(getpid(), Core::MachPort::adopt_right(self_send_port, Core::MachPort::PortRight::Send));
  63. #endif
  64. }
  65. ProcessManager::~ProcessManager()
  66. {
  67. Core::EventLoop::unregister_signal(m_signal_handle);
  68. }
  69. Optional<Process&> ProcessManager::find_process(pid_t pid)
  70. {
  71. return m_processes.get(pid);
  72. }
  73. void ProcessManager::add_process(WebView::Process&& process)
  74. {
  75. Threading::MutexLocker locker { m_lock };
  76. auto pid = process.pid();
  77. auto result = m_processes.set(pid, move(process));
  78. VERIFY(result == AK::HashSetResult::InsertedNewEntry);
  79. m_statistics.processes.append(make<Core::Platform::ProcessInfo>(pid));
  80. }
  81. #if defined(AK_OS_MACH)
  82. void ProcessManager::set_process_mach_port(pid_t pid, Core::MachPort&& port)
  83. {
  84. Threading::MutexLocker locker { m_lock };
  85. for (auto const& info : m_statistics.processes) {
  86. if (info->pid == pid) {
  87. info->child_task_port = move(port);
  88. return;
  89. }
  90. }
  91. }
  92. #endif
  93. Optional<Process> ProcessManager::remove_process(pid_t pid)
  94. {
  95. Threading::MutexLocker locker { m_lock };
  96. m_statistics.processes.remove_first_matching([&](auto const& info) {
  97. return (info->pid == pid);
  98. });
  99. return m_processes.take(pid);
  100. }
  101. void ProcessManager::update_all_process_statistics()
  102. {
  103. Threading::MutexLocker locker { m_lock };
  104. (void)update_process_statistics(m_statistics);
  105. }
  106. String ProcessManager::generate_html()
  107. {
  108. Threading::MutexLocker locker { m_lock };
  109. StringBuilder builder;
  110. builder.append(R"(
  111. <html>
  112. <head>
  113. <title>Task Manager</title>
  114. <style>
  115. @media (prefers-color-scheme: dark) {
  116. /* FIXME: We should be able to remove the HTML style when "color-scheme" is supported */
  117. html {
  118. background-color: rgb(30, 30, 30);
  119. color: white;
  120. }
  121. tr:nth-child(even) {
  122. background: rgb(57, 57, 57);
  123. }
  124. }
  125. @media (prefers-color-scheme: light) {
  126. tr:nth-child(even) {
  127. background: #f7f7f7;
  128. }
  129. }
  130. table {
  131. width: 100%;
  132. border-collapse: collapse;
  133. }
  134. th {
  135. text-align: left;
  136. border-bottom: 1px solid #aaa;
  137. }
  138. td, th {
  139. padding: 4px;
  140. border: 1px solid #aaa;
  141. }
  142. </style>
  143. </head>
  144. <body>
  145. <table>
  146. <thead>
  147. <tr>
  148. <th>Name</th>
  149. <th>PID</th>
  150. <th>Memory Usage</th>
  151. <th>CPU %</th>
  152. </tr>
  153. </thead>
  154. <tbody>
  155. )"sv);
  156. m_statistics.for_each_process([&](auto const& process) {
  157. builder.append("<tr>"sv);
  158. builder.append("<td>"sv);
  159. auto& process_handle = this->find_process(process.pid).value();
  160. builder.append(WebView::process_name_from_type(process_handle.type()));
  161. if (process_handle.title().has_value())
  162. builder.appendff(" - {}", escape_html_entities(*process_handle.title()));
  163. builder.append("</td>"sv);
  164. builder.append("<td>"sv);
  165. builder.append(MUST(String::number(process.pid)));
  166. builder.append("</td>"sv);
  167. builder.append("<td>"sv);
  168. builder.append(human_readable_size(process.memory_usage_bytes));
  169. builder.append("</td>"sv);
  170. builder.append("<td>"sv);
  171. builder.append(MUST(String::formatted("{:.1f}", process.cpu_percent)));
  172. builder.append("</td>"sv);
  173. builder.append("</tr>"sv);
  174. });
  175. builder.append(R"(
  176. </tbody>
  177. </table>
  178. </body>
  179. </html>
  180. )"sv);
  181. return builder.to_string_without_validation();
  182. }
  183. }