diff options
| author | Assaf Gordon <assafgordon@gmail.com> | 2017-07-10 22:50:01 +0000 |
|---|---|---|
| committer | Assaf Gordon <assafgordon@gmail.com> | 2017-08-06 22:07:16 -0600 |
| commit | fe24b572fca28dc9922096d21b665736dc477534 (patch) | |
| tree | 6dfed2eb28ec96e51b3d65878cd6772da38386a2 | |
| parent | 6e1f7d1ef7eb644ed5e3372d552bf265ff1f2ef2 (diff) | |
| download | time-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.am | 3 | ||||
| -rw-r--r-- | bootstrap.conf | 1 | ||||
| -rw-r--r-- | configure.ac | 58 | ||||
| -rw-r--r-- | src/rusage-kb.c | 62 | ||||
| -rw-r--r-- | src/rusage-kb.h | 90 | ||||
| -rw-r--r-- | src/time.c | 105 |
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_ */ @@ -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; |
