GNU time's configure script has the following:

    # What memory units are reported by getrusage(2) ?
    warn_getrusage_mem_units=
    if test -z "$time_getrusage_mem_units" ; then
      # if envvar 'time_getrusage_mem_units' isn't set,
      # autodetect based on OS.
      case "$host_os" in
        minix*|aix*|*bsd*|linux*|gnu*)
                         time_getrusage_mem_units=kb ;;
    
        macos*|darwin*)  time_getrusage_mem_units=bytes ;;
    
        solaris*)        time_getrusage_mem_units=pages ;;
    
        # As a fallback, assume KB (the most common value).
        # Set the 'warn' variable to warn the user at the end
        # of ./configure
        *) time_getrusage_mem_units=kb
           warn_getrusage_mem_units=yes
           ;;
      esac
    fi

Using the attached test program we can test what unit a platform uses.

On GNU/Linux, AIX 7.3, FreeBSD 15.0, NetBSD 10.0, and OpenBSD 7.8:

    $ gcc test-getrusage-maxrss.c
    $ ./a.out
    Kilobytes

On macOS 26:

    $ gcc test-getrusage-maxrss.c
    $ ./a.out
    Bytes

On Solaris 11.4:

    $ gcc test-getrusage-maxrss.c
    $ ./a.out
    Empty

Solaris 11's man page says that it is measured in pages, but also that
it doesn't fill it. I guess maybe they meant to document the behavior on
older Solaris versions.

It might be worth Gnulib working around that, but before that is
considered I would have to talk to the GNU Octave maintainers who also
use this module. In the meantime, it is probably worth documenting with
the attached patch.

Collin

>From b3fea86dc3e0d44ef8044013d2f9d6cfd3801892 Mon Sep 17 00:00:00 2001
Message-ID: <b3fea86dc3e0d44ef8044013d2f9d6cfd3801892.1769061375.git.collin.fu...@gmail.com>
From: Collin Funk <[email protected]>
Date: Wed, 21 Jan 2026 21:43:56 -0800
Subject: [PATCH] doc: document a portability issue with macOS getrusage.

* doc/posix-functions/getrusage.texi: Document that the 'ru_maxrss'
field of 'struct rusage' is measured in bytes on macOS.
---
 ChangeLog                          | 6 ++++++
 doc/posix-functions/getrusage.texi | 4 ++++
 2 files changed, 10 insertions(+)

diff --git a/ChangeLog b/ChangeLog
index 7b4a86c977..332817350a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2026-01-21  Collin Funk  <[email protected]>
+
+	doc: document a portability issue with macOS getrusage.
+	* doc/posix-functions/getrusage.texi: Document that the 'ru_maxrss'
+	field of 'struct rusage' is measured in bytes on macOS.
+
 2026-01-20  Bruno Haible  <[email protected]>
 
 	vasnprintf: Optimize when 'long double' is the same as 'double'.
diff --git a/doc/posix-functions/getrusage.texi b/doc/posix-functions/getrusage.texi
index 8993a77fa1..18c1c47734 100644
--- a/doc/posix-functions/getrusage.texi
+++ b/doc/posix-functions/getrusage.texi
@@ -19,4 +19,8 @@ @node getrusage
 @item
 Many platforms don't fill in all the fields of @code{struct rusage} with
 meaningful values.
+@item
+Some platforms fill the @code{ru_maxrss} field of @code{struct rusage}
+with the maximum resident set size in bytes instead of kilobytes:
+macOS 26.
 @end itemize
-- 
2.52.0

#include <stdio.h>
#include <sys/resource.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <limits.h>
#define PAGES 1000
int
main (void)
{
  pid_t pid = fork ();
  if (pid < 0)
    {
      fprintf (stderr, "fork: %s\n", strerror (errno));
      return EXIT_FAILURE;
    }
  else if (0 < pid)
    {
      /* Parent.  */
      int status;
      int result = waitpid (-1, &status, 0);
      if (result < 0)
        {
          fprintf (stderr, "waitpid: %s\n", strerror (errno));
          return EXIT_FAILURE;
        }
      if (! WIFEXITED (status) || WEXITSTATUS (status))
        {
          fprintf (stderr, "child exited abnormally\n");
          return EXIT_FAILURE;
        }
      struct rusage ru;
      if (getrusage (RUSAGE_CHILDREN, &ru) < 0)
        {
          fprintf (stderr, "getrusage: %s\n", strerror (errno));
          return EXIT_FAILURE;
        }
      int expect = getpagesize () * PAGES;
      int values[] = { ru.ru_maxrss, ru.ru_maxrss * 1024, ru.ru_maxrss * 
getpagesize () };
      int closest = abs (values[0] - expect);
      int index = 0;
      for (int i = 1; i < sizeof values / sizeof *values; ++i)
        {
          int diff = abs (values[i] - expect);
          if (diff < closest)
            {
              closest = diff;
              index = i;
            }
        }
      char const *sizes[] = { "Bytes", "Kilobytes", "Pages" };
      if (ru.ru_maxrss == 0)
        printf ("Empty\n");
      else
        printf ("%s\n", sizes[index]);
      return EXIT_SUCCESS;
    }
  else
    {
      /* Child.  */
      int bytes = getpagesize () * PAGES;
      char *pages = malloc (bytes);
      for (int i = 0; i < bytes; ++i)
        pages[i] = i;
      return EXIT_SUCCESS;
    }
}

Reply via email to