summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAssaf Gordon <assafgordon@gmail.com>2017-07-10 22:50:01 +0000
committerAssaf Gordon <assafgordon@gmail.com>2017-08-06 22:07:16 -0600
commitfe24b572fca28dc9922096d21b665736dc477534 (patch)
tree6dfed2eb28ec96e51b3d65878cd6772da38386a2
parent6e1f7d1ef7eb644ed5e3372d552bf265ff1f2ef2 (diff)
downloadtime-fe24b572fca28dc9922096d21b665736dc477534.tar.gz
time: adjust pages-vs-kb calculations for modern systems
* configure.ac: Define GETRUSAGE_RETURNS_{KB,BYTES,PAGES} based on $host_os. * bootstrap.conf: Add gnulib's inttpyes (for PRIuMAX). * src/rusage-kb.{c,h}: Wrapper functions to convert 'struct rusage' memory fields to kilobytes, based on GETRUSAGE_RETURNS_*. The ptok() function moved here (and is conditionally compiled). * Makefile.am: Add rusage-kb.{c,h}. * src/time.c: Use new wrapper functions to print memory usage.
-rw-r--r--Makefile.am3
-rw-r--r--bootstrap.conf1
-rw-r--r--configure.ac58
-rw-r--r--src/rusage-kb.c62
-rw-r--r--src/rusage-kb.h90
-rw-r--r--src/time.c105
6 files changed, 254 insertions, 65 deletions
diff --git a/Makefile.am b/Makefile.am
index 7072bb4..3b7be2a 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -37,7 +37,8 @@ bin_PROGRAMS = time
time_SOURCES = \
src/time.c \
- src/resuse.c src/resuse.h
+ src/resuse.c src/resuse.h \
+ src/rusage-kb.h src/rusage-kb.c
time_CFLAGS = $(WARN_CFLAGS)
diff --git a/bootstrap.conf b/bootstrap.conf
index 7ba0b16..ef126bd 100644
--- a/bootstrap.conf
+++ b/bootstrap.conf
@@ -28,6 +28,7 @@ gnulib_modules="
gettimeofday
gitlog-to-changelog
git-version-gen
+ inttypes
limits-h
non-recursive-gnulib-prefix-hack
stdlib
diff --git a/configure.ac b/configure.ac
index 202530a..f07b2d3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -67,6 +67,56 @@ AC_FUNC_VPRINTF
AC_FUNC_WAIT3
AC_CHECK_FUNCS(strerror)
+
+# 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
+
+# Define the configuration item
+case $time_getrusage_mem_units in
+ kb)
+ AC_DEFINE([GETRUSAGE_RETURNS_KB],[1],
+ [Define to 1 if getrusage(2) on this systems returns
+ ru_maxrss in kilobytes])
+ ;;
+
+ bytes)
+ AC_DEFINE([GETRUSAGE_RETURNS_BYTES],[1],
+ [Define to 1 if getrusage(2) on this systems returns
+ ru_maxrss in bytes])
+ ;;
+
+ pages)
+ AC_DEFINE([GETRUSAGE_RETURNS_PAGES],[1],
+ [Define to 1 if getrusage(2) on this systems returns
+ ru_maxrss in pages])
+ ;;
+
+ *) # should never happen
+ AC_MSG_ERROR([invalid 'time_getrusage_mem_units' value
+ '$time_getrusage_mem_units'])
+ ;;
+esac
+
+
# This is needed when building outside the source dir
# with --disable-dependency-tracking.
# Inspired by sed's https://bugs.gnu.org/25371
@@ -77,3 +127,11 @@ AS_MKDIR_P([tests])
AC_CONFIG_FILES([Makefile])
AC_OUTPUT
+
+# Warn the user if getrusage(2) behaviour on this OS is unknown
+if test "$warn_getrusage_mem_units" ; then
+ AC_MSG_WARN([unknown getrusage behavior on operating system '$host_os'.
+ Assuming Kilobytes.
+ please report this with the output of 'uname -a' to
+ bug-time@gnu.org])
+fi
diff --git a/src/rusage-kb.c b/src/rusage-kb.c
new file mode 100644
index 0000000..39ccd44
--- /dev/null
+++ b/src/rusage-kb.c
@@ -0,0 +1,62 @@
+/* resuse.h - declarations for child process resource use library
+ Copyright (C) 2017 Free Software Foundation, Inc.
+
+ This file is part of GNU Time.
+
+ GNU Time is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ GNU Time is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Time. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "config.h"
+
+#if GETRUSAGE_RETURNS_PAGES
+
+#include <sys/types.h>
+#include <limits.h>
+#include <unistd.h>
+
+/* Return the number of kilobytes corresponding to a number of pages PAGES.
+ (Actually, we use it to convert pages*ticks into kilobytes*ticks.)
+
+ Try to do arithmetic so that the risk of overflow errors is minimized.
+ This is funky since the pagesize could be less than 1K.
+ Note: Some machines express getrusage statistics in terms of K,
+ others in terms of pages. */
+unsigned long
+ptok (unsigned long pages)
+{
+ static unsigned long ps = 0;
+ unsigned long tmp;
+ static long size = LONG_MAX;
+
+ /* Initialization. */
+ if (ps == 0)
+ ps = (long) getpagesize ();
+
+ /* Conversion. */
+ if (pages > (LONG_MAX / ps))
+ {
+ /* Could overflow. */
+ tmp = pages / 1024; /* Smaller first, */
+ size = tmp * ps; /* then larger. */
+ }
+ else
+ {
+ /* Could underflow. */
+ tmp = pages * ps; /* Larger first, */
+ size = tmp / 1024; /* then smaller. */
+ }
+ return size;
+}
+
+#endif /* !GETRUSAGE_RETURNS_PAGES */
diff --git a/src/rusage-kb.h b/src/rusage-kb.h
new file mode 100644
index 0000000..b9cbaae
--- /dev/null
+++ b/src/rusage-kb.h
@@ -0,0 +1,90 @@
+/* resuse.h - declarations for child process resource use library
+ Copyright (C) 2017 Free Software Foundation, Inc.
+
+ This file is part of GNU Time.
+
+ GNU Time is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ GNU Time is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Time. If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef _RUSAGE_KB_
+#define _RUSAGE_KB_
+
+/* As of 2017, most kernels' getrusage(2) returns ru_maxrss in kilobytes:
+ Linux, Hurd, Free/Open/Net-BSD, MINIX, AIX7
+
+ OpenSolaris's getrusage(2) documents a return value in pages,
+ but it also says:
+ "ru_maxrss, ru_ixrss, ru_idrss, and ru_isrss [...]
+ are set to 0 in this implementation"
+
+ GETRUSAGE_RETURNS_KB is set in configure.ac .
+*/
+
+#if GETRUSAGE_RETURNS_KB
+
+/* define as no-op, as RUSAGE values are already in KB */
+#define RUSAGE_MEM_TO_KB(x) (x)
+
+#elif GETRUSAGE_RETURNS_BYTES
+
+/* Convert bytes to kilobytes */
+#define RUSAGE_MEM_TO_KB(x) ((x)/1024)
+
+#elif GETRUSAGE_RETURNS_PAGES
+
+/* Convert bytes to kilobytes */
+#define RUSAGE_MEM_TO_KB(x) (ptok (x))
+
+/* A function to get the system's page size and convert pages to KB */
+unsigned long
+ptok (unsigned long pages);
+
+#else
+
+#error "configuration error: no GETRUSAGE_RETURNS_{KB,BYTES,PAGES} defined"
+
+#endif
+
+
+/* Accessor functions.
+
+ These also convert the value to uintmax_t, alleviating the need to
+ worry about the type of ru->ru_maxrss (e.g. long, unsigned long,
+ long long, etc),
+ and on 64-bit systems 'uintmax_t' will be 64bit, reducing the need
+ to worry about overflow in case of very large memory sizes. */
+static inline uintmax_t
+get_rusage_maxrss_kb (const struct rusage *ru)
+{
+ return (uintmax_t)RUSAGE_MEM_TO_KB (ru->ru_maxrss);
+}
+
+static inline uintmax_t
+get_rusage_ixrss_kb (const struct rusage *ru)
+{
+ return (uintmax_t)RUSAGE_MEM_TO_KB (ru->ru_ixrss);
+}
+
+static inline uintmax_t
+get_rusage_idrss_kb (const struct rusage *ru)
+{
+ return (uintmax_t)RUSAGE_MEM_TO_KB (ru->ru_idrss);
+}
+
+static inline uintmax_t
+get_rusage_isrss_kb (const struct rusage *ru)
+{
+ return (uintmax_t)RUSAGE_MEM_TO_KB (ru->ru_isrss);
+}
+
+#endif /* _RUSAGE_KB_ */
diff --git a/src/time.c b/src/time.c
index d08bef1..8bb1c6e 100644
--- a/src/time.c
+++ b/src/time.c
@@ -29,6 +29,7 @@
#include <stdio.h>
#include <signal.h>
#include <getopt.h>
+#include <inttypes.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
@@ -39,7 +40,7 @@
#include "version-etc.h"
#include "resuse.h"
-
+#include "rusage-kb.h"
@@ -377,39 +378,7 @@ linear_argv (argv)
return new;
}
-/* Return the number of kilobytes corresponding to a number of pages PAGES.
- (Actually, we use it to convert pages*ticks into kilobytes*ticks.)
- Try to do arithmetic so that the risk of overflow errors is minimized.
- This is funky since the pagesize could be less than 1K.
- Note: Some machines express getrusage statistics in terms of K,
- others in terms of pages. */
-
-static unsigned long
-ptok (pages)
- unsigned long pages;
-{
- static unsigned long ps = 0;
- unsigned long tmp;
- static long size = LONG_MAX;
-
- /* Initialization. */
- if (ps == 0)
- ps = (long) getpagesize ();
-
- /* Conversion. */
- if (pages > (LONG_MAX / ps))
- { /* Could overflow. */
- tmp = pages / 1024; /* Smaller first, */
- size = tmp * ps; /* then larger. */
- }
- else
- { /* Could underflow. */
- tmp = pages * ps; /* Larger first, */
- size = tmp / 1024; /* then smaller. */
- }
- return size;
-}
/* summarize: Report on the system use of a command.
@@ -500,12 +469,12 @@ summarize (fp, fmt, command, resp)
case 'C': /* The command that got timed. */
fprintargv (fp, command, " ");
break;
- case 'D': /* Average unshared data size. */
- fprintf (fp, "%lu",
- MSEC_TO_TICKS (v) == 0 ? 0 :
- ptok ((UL) resp->ru.ru_idrss) / MSEC_TO_TICKS (v) +
- ptok ((UL) resp->ru.ru_isrss) / MSEC_TO_TICKS (v));
- break;
+ case 'D': /* Average unshared data size. */
+ fprintf (fp, "%" PRIuMAX,
+ MSEC_TO_TICKS (v) == 0 ? 0 :
+ get_rusage_idrss_kb (&resp->ru) / MSEC_TO_TICKS (v) +
+ get_rusage_isrss_kb (&resp->ru) / MSEC_TO_TICKS (v));
+ break;
case 'E': /* Elapsed real (wall clock) time. */
if (resp->elapsed.tv_sec >= 3600) /* One hour -> h:m:s. */
fprintf (fp, "%ld:%02ld:%02ld",
@@ -524,16 +493,18 @@ summarize (fp, fmt, command, resp)
case 'I': /* Inputs. */
fprintf (fp, "%ld", resp->ru.ru_inblock);
break;
- case 'K': /* Average mem usage == data+stack+text. */
- fprintf (fp, "%lu",
- MSEC_TO_TICKS (v) == 0 ? 0 :
- ptok ((UL) resp->ru.ru_idrss) / MSEC_TO_TICKS (v) +
- ptok ((UL) resp->ru.ru_isrss) / MSEC_TO_TICKS (v) +
- ptok ((UL) resp->ru.ru_ixrss) / MSEC_TO_TICKS (v));
- break;
- case 'M': /* Maximum resident set size. */
- fprintf (fp, "%lu", ptok ((UL) resp->ru.ru_maxrss));
- break;
+
+ case 'K': /* Average mem usage == data+stack+text. */
+ fprintf (fp, "%lu",
+ MSEC_TO_TICKS (v) == 0 ? 0 :
+ get_rusage_idrss_kb (&resp->ru) / MSEC_TO_TICKS (v) +
+ get_rusage_isrss_kb (&resp->ru) / MSEC_TO_TICKS (v) +
+ get_rusage_ixrss_kb (&resp->ru) / MSEC_TO_TICKS (v));
+ break;
+ case 'M': /* Maximum resident set size. */
+ fprintf (fp, "%" PRIuMAX, get_rusage_maxrss_kb (&resp->ru));
+ break;
+
case 'O': /* Outputs. */
fprintf (fp, "%ld", resp->ru.ru_oublock);
break;
@@ -560,11 +531,13 @@ summarize (fp, fmt, command, resp)
case 'W': /* Times swapped out. */
fprintf (fp, "%ld", resp->ru.ru_nswap);
break;
- case 'X': /* Average shared text size. */
- fprintf (fp, "%lu",
- MSEC_TO_TICKS (v) == 0 ? 0 :
- ptok ((UL) resp->ru.ru_ixrss) / MSEC_TO_TICKS (v));
- break;
+
+ case 'X': /* Average shared text size. */
+ fprintf (fp, "%" PRIuMAX,
+ MSEC_TO_TICKS (v) == 0 ? 0 :
+ get_rusage_ixrss_kb (&resp->ru) / MSEC_TO_TICKS (v));
+ break;
+
case 'Z': /* Page size. */
fprintf (fp, "%d", getpagesize ());
break;
@@ -579,22 +552,26 @@ summarize (fp, fmt, command, resp)
case 'k': /* Signals delivered. */
fprintf (fp, "%ld", resp->ru.ru_nsignals);
break;
- case 'p': /* Average stack segment. */
- fprintf (fp, "%lu",
- MSEC_TO_TICKS (v) == 0 ? 0 :
- ptok ((UL) resp->ru.ru_isrss) / MSEC_TO_TICKS (v));
- break;
+
+ case 'p': /* Average stack segment. */
+ fprintf (fp, "%"PRIuMAX,
+ MSEC_TO_TICKS (v) == 0 ? 0 :
+ get_rusage_isrss_kb (&resp->ru) / MSEC_TO_TICKS (v));
+ break;
+
case 'r': /* Incoming socket messages received. */
fprintf (fp, "%ld", resp->ru.ru_msgrcv);
break;
case 's': /* Outgoing socket messages sent. */
fprintf (fp, "%ld", resp->ru.ru_msgsnd);
break;
- case 't': /* Average resident set size. */
- fprintf (fp, "%lu",
- MSEC_TO_TICKS (v) == 0 ? 0 :
- ptok ((UL) resp->ru.ru_idrss) / MSEC_TO_TICKS (v));
- break;
+
+ case 't': /* Average resident set size. */
+ fprintf (fp, "%" PRIuMAX,
+ MSEC_TO_TICKS (v) == 0 ? 0 :
+ get_rusage_idrss_kb (&resp->ru) / MSEC_TO_TICKS (v));
+ break;
+
case 'w': /* Voluntary context switches. */
fprintf (fp, "%ld", resp->ru.ru_nvcsw);
break;