ProcessStatisticsMach.cpp 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. /*
  2. * Copyright (c) 2024, Andrew Kaster <akaster@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/Platform.h>
  7. #if !defined(AK_OS_MACH)
  8. # error "This file is only available on Mach platforms"
  9. #endif
  10. #include <AK/ByteString.h>
  11. #include <AK/Time.h>
  12. #include <LibCore/MachPort.h>
  13. #include <LibCore/Platform/ProcessStatisticsMach.h>
  14. namespace Core::Platform {
  15. static auto user_hz = sysconf(_SC_CLK_TCK);
  16. ErrorOr<void> update_process_statistics(ProcessStatistics& statistics)
  17. {
  18. host_cpu_load_info_data_t cpu_info {};
  19. mach_msg_type_number_t count = HOST_CPU_LOAD_INFO_COUNT;
  20. auto res = host_statistics(mach_host_self(), HOST_CPU_LOAD_INFO, reinterpret_cast<host_info_t>(&cpu_info), &count);
  21. if (res != KERN_SUCCESS) {
  22. dbgln("Failed to get host statistics: {}", mach_error_string(res));
  23. return Core::mach_error_to_error(res);
  24. }
  25. u64 total_cpu_ticks = 0;
  26. total_cpu_ticks += cpu_info.cpu_ticks[CPU_STATE_USER];
  27. total_cpu_ticks += cpu_info.cpu_ticks[CPU_STATE_SYSTEM];
  28. total_cpu_ticks += cpu_info.cpu_ticks[CPU_STATE_NICE];
  29. total_cpu_ticks += cpu_info.cpu_ticks[CPU_STATE_IDLE];
  30. auto const total_cpu_ticks_diff = total_cpu_ticks - statistics.total_time_scheduled;
  31. auto const total_cpu_seconds_diff = total_cpu_ticks_diff / (static_cast<float>(user_hz));
  32. auto const total_cpu_micro_diff = total_cpu_seconds_diff * 1'000'000;
  33. statistics.total_time_scheduled = total_cpu_ticks;
  34. for (auto& process : statistics.processes) {
  35. mach_task_basic_info_data_t basic_info {};
  36. count = MACH_TASK_BASIC_INFO_COUNT;
  37. res = task_info(process->child_task_port.port(), MACH_TASK_BASIC_INFO, reinterpret_cast<task_info_t>(&basic_info), &count);
  38. if (res != KERN_SUCCESS) {
  39. dbgln("Failed to get task info for pid {}: {}", process->pid, mach_error_string(res));
  40. return Core::mach_error_to_error(res);
  41. }
  42. process->memory_usage_bytes = basic_info.resident_size;
  43. task_thread_times_info_data_t time_info {};
  44. count = TASK_THREAD_TIMES_INFO_COUNT;
  45. res = task_info(process->child_task_port.port(), TASK_THREAD_TIMES_INFO, reinterpret_cast<task_info_t>(&time_info), &count);
  46. if (res != KERN_SUCCESS) {
  47. dbgln("Failed to get thread times info for pid {}: {}", process->pid, mach_error_string(res));
  48. return Core::mach_error_to_error(res);
  49. }
  50. timeval scratch_timeval = { static_cast<time_t>(time_info.user_time.seconds), static_cast<suseconds_t>(time_info.user_time.microseconds) };
  51. auto time_in_process = Duration::from_timeval(scratch_timeval);
  52. scratch_timeval = { static_cast<time_t>(time_info.system_time.seconds), static_cast<suseconds_t>(time_info.system_time.microseconds) };
  53. time_in_process += Duration::from_timeval(scratch_timeval);
  54. auto time_diff_process = time_in_process - Duration::from_microseconds(process->time_spent_in_process);
  55. process->time_spent_in_process = time_in_process.to_microseconds();
  56. process->cpu_percent = 0.0f;
  57. if (time_diff_process > Duration::zero())
  58. process->cpu_percent = 100.0f * static_cast<float>(time_diff_process.to_microseconds()) / total_cpu_micro_diff;
  59. }
  60. return {};
  61. }
  62. void register_with_mach_server(ByteString const& server_name)
  63. {
  64. auto server_port_or_error = Core::MachPort::look_up_from_bootstrap_server(server_name);
  65. if (server_port_or_error.is_error()) {
  66. dbgln("Failed to lookup server port: {}", server_port_or_error.error());
  67. return;
  68. }
  69. auto server_port = server_port_or_error.release_value();
  70. // Send our own task port to the server so they can query statistics about us
  71. ChildPortMessage message {};
  72. message.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSGH_BITS_ZERO) | MACH_MSGH_BITS_COMPLEX;
  73. message.header.msgh_size = sizeof(message);
  74. message.header.msgh_remote_port = server_port.port();
  75. message.header.msgh_local_port = MACH_PORT_NULL;
  76. message.header.msgh_id = SELF_TASK_PORT_MESSAGE_ID;
  77. message.body.msgh_descriptor_count = 1;
  78. message.port_descriptor.name = mach_task_self();
  79. message.port_descriptor.disposition = MACH_MSG_TYPE_COPY_SEND;
  80. message.port_descriptor.type = MACH_MSG_PORT_DESCRIPTOR;
  81. mach_msg_timeout_t const timeout = 100; // milliseconds
  82. auto const send_result = mach_msg(&message.header, MACH_SEND_MSG | MACH_SEND_TIMEOUT, message.header.msgh_size, 0, MACH_PORT_NULL, timeout, MACH_PORT_NULL);
  83. if (send_result != KERN_SUCCESS) {
  84. dbgln("Failed to send message to server: {}", mach_error_string(send_result));
  85. return;
  86. }
  87. }
  88. }