Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

darwin: Fix memory metrics support for Apple Silicon (ARM64) #1439

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

SuCicada
Copy link

@SuCicada SuCicada commented Apr 8, 2024

Fix memory metrics of Apple Silicon (ARM64)
Detailed description: #631

The calculation of memory refers to exelban/stats Modules/RAM/readers.swift

let used = active + inactive + speculative + wired + compressed - purgeable - external

@SuCicada SuCicada marked this pull request as ready for review April 8, 2024 09:50
@SuCicada SuCicada changed the title darwin: Enhance memory metrics support for Apple Silicon (ARM64) darwin: Fix memory metrics support for Apple Silicon (ARM64) Apr 8, 2024
@BenBE BenBE added bug 🐛 Something isn't working MacOS 🍏 MacOS / Darwin related issues labels Apr 8, 2024
@BenBE BenBE added this to the 3.4.0 milestone Apr 8, 2024
@@ -19,6 +19,9 @@ typedef struct DarwinMachine_ {

host_basic_info_data_t host_info;
vm_statistics_data_t vm_stats;
#if defined(__arm64__)
vm_statistics64_data_t vm_stats64;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This structure does not seem to be exclusive to ARM64. Would it be used in Intel Macs, too? A reference to Apple documentation would be useful.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right. According to the documentation:
https://developer.apple.com/documentation/kernel/vm_statistics64_data_t
https://developer.apple.com/documentation/kernel/vm_statistics_data_t

vm_statistics64_data_t can be used after macOS 10.6+
vm_statistics_data_t can be used after macOS 10.0+

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@SuCicada I prefer keeping a fallback mechanism when vm_statistics64_data_t is not available. There is no reason to remove 32-bit Mac support (htop documentation doesn't state a minimum required version of macOS).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haven't looked at the details of the implementation yet, but would it be feasible to check for vm_statistics64_data_t and vm_statistics_data_t in configure and use the 64 bit version everywhere when available? And if none of both is available, we either fall back entirely or bail out at compile time.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@BenBE Since it is said that vm_statistics_data_t is available in OS X 10.0 (the very first Mac OS X), these is no need to check this one in particular in configure. Just checking for vm_statistics64_data_t is enough.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I will restore support for vm state 32.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exactly: Use vm_statistics64_data_t when available, otherwise fall back to vm_statistics_data_t.

@@ -75,6 +85,9 @@ void Machine_scan(Machine* super) {
host->prev_load = host->curr_load;
DarwinMachine_allocateCPULoadInfo(&host->curr_load);
DarwinMachine_getVMStats(&host->vm_stats);
#if defined(__arm64__)
DarwinMachine_getVMStats64(&host->vm_stats64);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason to duplicate the effort of retrieving both vm_stats structures?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to prevent affecting existing functionality, but I checked the code again and it didn't affect anywhere else.
Same as above. I fix that.

@@ -67,6 +67,16 @@ static void DarwinMachine_getVMStats(vm_statistics_t p) {
}
}

#if defined(__arm64__)
static void DarwinMachine_getVMStats64(vm_statistics64_t p) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider merging this function with DarwinMachine_getVMStats above, so as to avoid duplicate code.

You can use "DarwinMachine* this" as the input argument.

@@ -290,6 +290,24 @@ double Platform_setCPUValues(Meter* mtr, unsigned int cpu) {
return CLAMP(total, 0.0, 100.0);
}

#if defined(__arm64__)
void Platform_setMemoryValues(Meter *mtr) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider merging two Platform_setMemoryValues functions together.

double page_K = (double) vm_page_size / (double) 1024;

mtr->total = dhost->host_info.max_mem / 1024;
int used = vm->active_count + vm->inactive_count +
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it intentional to use signed int type here? Note that vm->active_count and similar members are in "natural_t" type, which is unsigned.

@BenBE
Copy link
Member

BenBE commented Apr 9, 2024

Two general notes:

  1. Squash the two commits, as commits should show a direct way from A to B without all the steps inside the maze … Mostly split commits, when there are independent changes that work without each other or there's something preparing for a later change

  2. There should be no blank after casts, like this (foo)expression

Also note the remarks earlier re implementation for both 32 and 64 bit VM structures using conditional compilation.

@SuCicada SuCicada deleted the branch htop-dev:main April 10, 2024 02:21
@SuCicada SuCicada closed this Apr 10, 2024
@SuCicada SuCicada deleted the main branch April 10, 2024 02:21
@SuCicada SuCicada restored the main branch April 10, 2024 02:21
@SuCicada SuCicada reopened this Apr 10, 2024
@SuCicada
Copy link
Author

SuCicada commented Apr 10, 2024

Thank you for your guidance, I have resubmitted.

  1. I fix the blank format
  2. I use __LP64__ for conditional compilation instead.
  3. I restored 32-bit support and removed duplicate functions that I had written before.

@BenBE
Copy link
Member

BenBE commented Apr 10, 2024

LGTM.

Any particular reason for checking an architecture setting (64 bit support of the architecture) over explicit testing for availability of a concrete type/structure in configure.ac?

@SuCicada
Copy link
Author

LGTM.

Any particular reason for checking an architecture setting (64 bit support of the architecture) over explicit testing for availability of a concrete type/structure in configure.ac?

Oh, You are right. It is more explicit to test the availability of a concrete type/structure in configure.ac.
I will try

@SuCicada
Copy link
Author

Thank you for your guidance, I have resubmitted.

  1. I added checking of vm_statistics64 at configure.ac.
  2. I use HAVE_VM_STATISTICS64 for conditional compilation instead.

What do you think

configure.ac Outdated
Comment on lines 333 to 347

AC_MSG_CHECKING([for vm_statistics64 supported])
AC_COMPILE_IFELSE([
AC_LANG_PROGRAM([[
#include <mach/mach_host.h>
#include <mach/vm_statistics.h>
]], [[
mach_msg_type_number_t info_size = HOST_VM_INFO64_COUNT;
vm_statistics64_data_t vm_stat;
host_statistics64(mach_host_self(), HOST_VM_INFO64, (host_info64_t)&vm_stat, &info_size);
]]
)],
AC_DEFINE([HAVE_VM_STATISTICS64], 1, [The vm_statistics64 features is supported.])
AC_MSG_RESULT(yes),
AC_MSG_RESULT(no))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use things like this (untested)?

Suggested change
AC_MSG_CHECKING([for vm_statistics64 supported])
AC_COMPILE_IFELSE([
AC_LANG_PROGRAM([[
#include <mach/mach_host.h>
#include <mach/vm_statistics.h>
]], [[
mach_msg_type_number_t info_size = HOST_VM_INFO64_COUNT;
vm_statistics64_data_t vm_stat;
host_statistics64(mach_host_self(), HOST_VM_INFO64, (host_info64_t)&vm_stat, &info_size);
]]
)],
AC_DEFINE([HAVE_VM_STATISTICS64], 1, [The vm_statistics64 features is supported.])
AC_MSG_RESULT(yes),
AC_MSG_RESULT(no))
AC_CHECK_FUNCS([host_statistics64], [], [], [[#include <mach/vm_statistics.h>]])
AC_CHECK_DECLS([vm_statistics64_data_t], [], [], [[#include <mach/vm_statistics.h>]])

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@BenBE You and I suggested the same thing at the same time...

By the way, AC_CHECK_DECLS is not for checking type declarations, use AC_CHECK_TYPES instead. See my other comment.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had a look at the AutoConf docs, but didn't see it there somehow …

configure.ac Outdated
@@ -330,6 +330,21 @@ if test "$my_htop_platform" = darwin; then
AC_CHECK_FUNCS([mach_timebase_info])
AC_CHECK_DECLS([IOMainPort], [], [], [[#include <IOKit/IOKitLib.h>]])
AC_CHECK_DECLS([IOMasterPort], [], [], [[#include <IOKit/IOKitLib.h>]])

AC_MSG_CHECKING([for vm_statistics64 supported])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought you can use AC_CHECK_TYPES macro to simplify the check.
Maybe this is sufficient:

AC_CHECK_TYPES([struct vm_statistics64], [], [], [[#include <mach/vm_statistics.h>]]
dnl This defines HAVE_STRUCT_VM_STATISTICS64

@SuCicada
Copy link
Author

Makes sense, but this will produce 2 macros. If use
If someone's computer cannot meet host_statistics64 and vm_statistics64_data_t at the same time, (very unlikely)
Then it will happen:

host_statistics64 support vm_statistics64_data_t support result
yes no can compile and run normally, but the memory value is incorrect
no yes can compile normally, but fails to run:
[1] 16517 segmentation fault ./htop

I am not sure if there is a better solution, or if we can ignore this very unlikely situation.

@BenBE
Copy link
Member

BenBE commented Apr 11, 2024

AutoConf has the ability to nest those macros, i.e. only check the second if the first succeeded. Likewise for the second macro: Only define the feature flag once the second macro succeeded too.

Untested:

   AC_CHECK_FUNCS([host_statistics64], [
      AC_CHECK_TYPES([struct vm_statistics64], [
         AC_DEFINE([HAVE_VM_STATISTICS64], 1, [The vm_statistics64 features is supported.])
      ], [], [[#include <mach/vm_statistics.h>]])
   ], [], [[#include <mach/vm_statistics.h>]])

Check for HAVE_VM_STATISTICS64 in the code …

@Explorer09
Copy link
Contributor

@BenBE
Both AC_CHECK_FUNCS and AC_CHECK_TYPES would define the "HAVE_" C macro names for you. There's no need to use AC_DEFINE manually.

There would be HAVE_STRUCT_VM_STATISTICS64 and HAVE_HOST_STATISTICS64 defined automatically. Just pick a name that sounds better in the C code. And put that macro check as an inner call in configure.ac.

AC_CHECK_TYPES([struct vm_statistics64], [
   AC_CHECK_FUNCS([host_statistics64])
   ], [], [[#include <mach/vm_statistics.h>]])
dnl Then use HAVE_HOST_STATISTICS64 in the C code

@BenBE
Copy link
Member

BenBE commented Apr 11, 2024

I know. The AC_DEFINE was there to use the other code AS-IS and have the implicit AND …

@SuCicada
Copy link
Author

Thank you for your patient guidance, I have resubmitted.

(In fact, I had previously attempted this method but mistakenly believed it didn't work🫣. After your confirmation, I have re-verified and got the correct compile result.👍)

Copy link
Member

@BenBE BenBE left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure about the changed memory calculation. There seems something off …

Comment on lines +303 to +310
#ifdef HAVE_STRUCT_VM_STATISTICS64
natural_t used = vm->active_count + vm->inactive_count +
vm->speculative_count + vm->wire_count +
vm->compressor_page_count - vm->purgeable_count - vm->external_page_count;
mtr->values[MEMORY_METER_USED] = (double)(used - vm->compressor_page_count) * page_K;
#else
mtr->values[MEMORY_METER_USED] = (double)(vm->active_count + vm->wire_count) * page_K;
#endif
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reasons for this calculation to differ between those two places, besides missing fields from the old struct?

Also, vm->compressor_page_count is substracted twice AFAICS.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As #631 and #955 said, the current memory calculation may have a bit problem.

I referenced the code of exelban/stats and GuillaumeGomez/sysinfo(used by ClementTsang/bottom mentioned by #955 )

There are some differences in details, but it seems that the main thing missing is adding vm->compressor_page_count

It seems that many people have raised the same issue of memory parameter display.
So I just want macOS to display the correct memory parameters. But there may be something I haven't considered.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug 🐛 Something isn't working MacOS 🍏 MacOS / Darwin related issues
Projects
None yet
3 participants