If a system's time is adjusted backwards, then elapsed
time could appear as negative. This yielded a negative
%CPU value. Alternately if zeros were suppressed ('0')
the result was a blank %CPU column. In both cases that
distortion would last for one display cycle or until a
user forced a display refresh via some keyboard input.
The original recommendation was trading gettimeofday()
for clock_gettime() using CLOCK_MONOTONIC. But on some
systems that might not be possible, forcing the use of
CLOCK_REALTIME instead. Not only would that complicate
the build system, but it may leave us with minus %CPU.
Another approach was to ensure that elapsed time could
never be negative. Of course, this produced distortion
of %CPU values but it would be proportionally correct.
This wasn't dissimilar to a distortion already present
should the time be adjusted forward or backward within
any 'remaining' top delay intervals. These aberrations
would be avoided with clock_gettime & CLOCK_MONOTONIC,
but that is a less than ideal solution as noted above.
This final solution, which originated down under, will
simply rely on the /proc/uptime seconds, which will be
immune to *any* tampering with the system clock. Thus,
we now have a fix for the distortion we didn't know we
suffered plus a negative %CPU that began this odyssey.
Thanks to:
sk.alvin.x@gmail.com, for the original effort
jcapik@redhat.com, for a heads up on CLOCK_MONOTONIC
csmall-procps@enc.com.au, for the best suggestion of all
Reference(s):
. original post/patch
http://www.freelists.org/post/procps/PATCH-top-use-clock-gettime-instead-of-gettimeofday
. heads up on CLOCK_MONOTONIC
http://www.freelists.org/post/procps/PATCH-top-use-clock-gettime-instead-of-gettimeofday,2
. the final solution
http://www.freelists.org/post/procps/PATCH-top-use-clock-gettime-instead-of-gettimeofday,11
Signed-off-by: Jim Warner <james.warner@comcast.net>
HST_t *h;
if (!this) {
- static struct timeval oldtimev;
- struct timeval timev;
- struct timezone timez;
+ static double uptime_sav;
+ double uptime_cur;
float et;
void *v;
- gettimeofday(&timev, &timez);
- et = (timev.tv_sec - oldtimev.tv_sec)
- + (float)(timev.tv_usec - oldtimev.tv_usec) / 1000000.0;
- oldtimev.tv_sec = timev.tv_sec;
- oldtimev.tv_usec = timev.tv_usec;
+ uptime(&uptime_cur, NULL);
+ et = uptime_cur - uptime_sav;
+ uptime_sav = uptime_cur;
// if in Solaris mode, adjust our scaling for all cpus
Frame_etscale = 100.0f / ((float)Hertz * (float)et * (Rc.mode_irixps ? 1 : smp_num_cpus));