ProcessStatisticsMach.cpp 4.7 KB

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