Request the realtime and monotonic clock times once per sample

Refactor the sample time code to make one call to gettimeofday
(aka the realtime clock in clock_gettime, when available) and
one to the monotonic clock.  Stores each in more appropriately
named ProcessList fields for ready access when needed.  Every
platform gets the opportunity to provide their own clock code,
and the existing Mac OS X specific code is moved below darwin
instead of in Compat.

A couple of leftover time(2) calls are converted to use these
ProcessList fields as well, instead of yet again sampling the
system clock.

Related to https://github.com/htop-dev/htop/pull/574
This commit is contained in:
Nathan Scott 2021-03-30 15:55:48 +11:00 committed by Sohaib
parent 421bdeec60
commit 356488aa53
25 changed files with 210 additions and 82 deletions

View File

@ -23,7 +23,7 @@ static void ClockMeter_updateValues(Meter* this) {
const ProcessList* pl = this->pl;
struct tm result;
const struct tm* lt = localtime_r(&pl->timestamp.tv_sec, &result);
const struct tm* lt = localtime_r(&pl->realtime.tv_sec, &result);
this->values[0] = lt->tm_hour * 60 + lt->tm_min;
strftime(this->txtBuffer, sizeof(this->txtBuffer), "%H:%M:%S", lt);
}

View File

@ -11,18 +11,12 @@ in the source distribution for its full text.
#include <errno.h>
#include <fcntl.h> // IWYU pragma: keep
#include <time.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h> // IWYU pragma: keep
#include "XUtils.h" // IWYU pragma: keep
#ifdef HAVE_HOST_GET_CLOCK_SERVICE
#include <mach/clock.h>
#include <mach/mach.h>
#endif
int Compat_faccessat(int dirfd,
const char* pathname,
@ -123,31 +117,3 @@ ssize_t Compat_readlinkat(int dirfd,
#endif
}
int Compat_clock_monotonic_gettime(struct timespec *tp) {
#if defined(HAVE_CLOCK_GETTIME)
return clock_gettime(CLOCK_MONOTONIC, tp);
#elif defined(HAVE_HOST_GET_CLOCK_SERVICE)
clock_serv_t cclock;
mach_timespec_t mts;
host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock);
clock_get_time(cclock, &mts);
mach_port_deallocate(mach_task_self(), cclock);
tp->tv_sec = mts.tv_sec;
tp->tv_nsec = mts.tv_nsec;
return 0;
#else
#error No Compat_clock_monotonic_gettime() implementation!
#endif
}

View File

@ -56,6 +56,4 @@ ssize_t Compat_readlinkat(int dirfd,
char* buf,
size_t bufsize);
int Compat_clock_monotonic_gettime(struct timespec *tp);
#endif /* HEADER_Compat */

View File

@ -23,7 +23,7 @@ static void DateMeter_updateValues(Meter* this) {
const ProcessList* pl = this->pl;
struct tm result;
const struct tm* lt = localtime_r(&pl->timestamp.tv_sec, &result);
const struct tm* lt = localtime_r(&pl->realtime.tv_sec, &result);
this->values[0] = lt->tm_yday;
int year = lt->tm_year + 1900;
if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0)) {

View File

@ -20,9 +20,10 @@ static const int DateTimeMeter_attributes[] = {
};
static void DateTimeMeter_updateValues(Meter* this) {
time_t t = time(NULL);
const ProcessList* pl = this->pl;
struct tm result;
const struct tm* lt = localtime_r(&t, &result);
const struct tm* lt = localtime_r(&pl->realtime.tv_sec, &result);
int year = lt->tm_year + 1900;
if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0)) {
this->total = 366;

View File

@ -34,7 +34,7 @@ static void DiskIOMeter_updateValues(Meter* this) {
const ProcessList* pl = this->pl;
static uint64_t cached_last_update;
uint64_t passedTimeInMs = pl->timestampMs - cached_last_update;
uint64_t passedTimeInMs = pl->realtimeMs - cached_last_update;
/* update only every 500ms */
if (passedTimeInMs > 500) {
@ -43,7 +43,7 @@ static void DiskIOMeter_updateValues(Meter* this) {
static uint64_t cached_msTimeSpend_total;
uint64_t diff;
cached_last_update = pl->timestampMs;
cached_last_update = pl->realtimeMs;
DiskIOData data;

View File

@ -134,6 +134,7 @@ myhtopheaders = \
# -----
linux_platform_headers = \
generic/gettime.h \
generic/hostname.h \
generic/uname.h \
linux/HugePageMeter.h \
@ -154,6 +155,7 @@ linux_platform_headers = \
zfs/ZfsCompressedArcMeter.h
linux_platform_sources = \
generic/gettime.c \
generic/hostname.c \
generic/uname.c \
linux/HugePageMeter.c \
@ -184,6 +186,7 @@ freebsd_platform_headers = \
freebsd/FreeBSDProcess.h \
freebsd/Platform.h \
freebsd/ProcessField.h \
generic/gettime.h \
generic/hostname.h \
generic/openzfs_sysctl.h \
generic/uname.h \
@ -195,6 +198,7 @@ freebsd_platform_sources = \
freebsd/Platform.c \
freebsd/FreeBSDProcessList.c \
freebsd/FreeBSDProcess.c \
generic/gettime.c \
generic/hostname.c \
generic/openzfs_sysctl.c \
generic/uname.c \
@ -215,6 +219,7 @@ dragonflybsd_platform_headers = \
dragonflybsd/DragonFlyBSDProcess.h \
dragonflybsd/Platform.h \
dragonflybsd/ProcessField.h \
generic/gettime.h \
generic/hostname.h \
generic/uname.h
@ -222,6 +227,7 @@ dragonflybsd_platform_sources = \
dragonflybsd/DragonFlyBSDProcessList.c \
dragonflybsd/DragonFlyBSDProcess.c \
dragonflybsd/Platform.c \
generic/gettime.c \
generic/hostname.c \
generic/uname.c
@ -235,6 +241,7 @@ endif
# -------
openbsd_platform_headers = \
generic/gettime.h \
generic/hostname.h \
generic/uname.h \
openbsd/OpenBSDProcessList.h \
@ -243,6 +250,7 @@ openbsd_platform_headers = \
openbsd/ProcessField.h
openbsd_platform_sources = \
generic/gettime.c \
generic/hostname.c \
generic/uname.c \
openbsd/OpenBSDProcessList.c \
@ -263,6 +271,7 @@ darwin_platform_headers = \
darwin/DarwinProcessList.h \
darwin/Platform.h \
darwin/ProcessField.h \
generic/gettime.h \
generic/hostname.h \
generic/openzfs_sysctl.h \
generic/uname.h \
@ -274,6 +283,7 @@ darwin_platform_sources = \
darwin/Platform.c \
darwin/DarwinProcess.c \
darwin/DarwinProcessList.c \
generic/gettime.c \
generic/hostname.c \
generic/openzfs_sysctl.c \
generic/uname.c \
@ -291,6 +301,7 @@ endif
# -------
solaris_platform_headers = \
generic/gettime.h \
generic/hostname.h \
generic/uname.h \
solaris/ProcessField.h \
@ -302,6 +313,7 @@ solaris_platform_headers = \
zfs/ZfsCompressedArcMeter.h
solaris_platform_sources = \
generic/gettime.c \
generic/hostname.c \
generic/uname.c \
solaris/Platform.c \
@ -320,12 +332,14 @@ endif
# -----------
unsupported_platform_headers = \
generic/gettime.h \
unsupported/Platform.h \
unsupported/ProcessField.h \
unsupported/UnsupportedProcess.h \
unsupported/UnsupportedProcessList.h
unsupported_platform_sources = \
generic/gettime.c \
unsupported/Platform.c \
unsupported/UnsupportedProcess.c \
unsupported/UnsupportedProcessList.c

View File

@ -314,10 +314,10 @@ static void GraphMeterMode_draw(Meter* this, int x, int y, int w) {
x += captionLen;
w -= captionLen;
if (!timercmp(&pl->timestamp, &(data->time), <)) {
if (!timercmp(&pl->realtime, &(data->time), <)) {
int globalDelay = this->pl->settings->delay;
struct timeval delay = { .tv_sec = globalDelay / 10, .tv_usec = (globalDelay - ((globalDelay / 10) * 10)) * 100000 };
timeradd(&pl->timestamp, &delay, &(data->time));
timeradd(&pl->realtime, &delay, &(data->time));
for (int i = 0; i < nValues - 1; i++)
data->values[i] = data->values[i + 1];

View File

@ -28,7 +28,7 @@ static void NetworkIOMeter_updateValues(Meter* this) {
const ProcessList* pl = this->pl;
static uint64_t cached_last_update = 0;
uint64_t passedTimeInMs = pl->timestampMs - cached_last_update;
uint64_t passedTimeInMs = pl->realtimeMs - cached_last_update;
/* update only every 500ms */
if (passedTimeInMs > 500) {
@ -38,7 +38,7 @@ static void NetworkIOMeter_updateValues(Meter* this) {
static uint64_t cached_txp_total;
uint64_t diff;
cached_last_update = pl->timestampMs;
cached_last_update = pl->realtimeMs;
NetworkIOData data;
hasData = Platform_getNetworkIO(&data);

View File

@ -467,14 +467,14 @@ void Process_toggleTag(Process* this) {
bool Process_isNew(const Process* this) {
assert(this->processList);
if (this->processList->scanTs >= this->seenTs) {
return this->processList->scanTs - this->seenTs <= 1000 * this->processList->settings->highlightDelaySecs;
if (this->processList->monotonicMs >= this->seenStampMs) {
return this->processList->monotonicMs - this->seenStampMs <= 1000 * (uint64_t)this->processList->settings->highlightDelaySecs;
}
return false;
}
bool Process_isTomb(const Process* this) {
return this->tombTs > 0;
return this->tombStampMs > 0;
}
bool Process_setPriority(Process* this, int priority) {

View File

@ -174,8 +174,8 @@ typedef struct Process_ {
/*
* Internal time counts for showing new and exited processes.
*/
time_t seenTs;
time_t tombTs;
uint64_t seenStampMs;
uint64_t tombStampMs;
/*
* Internal state for tree-mode.

View File

@ -10,11 +10,12 @@ in the source distribution for its full text.
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include "Compat.h"
#include "CRT.h"
#include "Hashtable.h"
#include "Macros.h"
#include "Platform.h"
#include "Vector.h"
#include "XUtils.h"
@ -35,7 +36,7 @@ ProcessList* ProcessList_init(ProcessList* this, const ObjectClass* klass, Users
// set later by platform-specific code
this->cpuCount = 0;
this->scanTs = 0;
this->monotonicMs = 0;
#ifdef HAVE_LIBHWLOC
this->topologyOk = false;
@ -131,7 +132,7 @@ void ProcessList_add(ProcessList* this, Process* p) {
p->processList = this;
// highlighting processes found in first scan by first scan marked "far in the past"
p->seenTs = this->scanTs;
p->seenStampMs = this->monotonicMs;
Vector_add(this->processes, p);
Hashtable_put(this->processTable, p->pid, p);
@ -582,8 +583,6 @@ Process* ProcessList_getProcess(ProcessList* this, pid_t pid, bool* preExisting,
}
void ProcessList_scan(ProcessList* this, bool pauseProcessUpdate) {
struct timespec now;
// in pause mode only gather global data for meters (CPU/memory/...)
if (pauseProcessUpdate) {
ProcessList_goThroughEntries(this, true);
@ -604,31 +603,29 @@ void ProcessList_scan(ProcessList* this, bool pauseProcessUpdate) {
this->runningTasks = 0;
// set scanTs
// set scan timestamp
static bool firstScanDone = false;
if (!firstScanDone) {
this->scanTs = 0;
if (firstScanDone) {
Platform_gettime_monotonic(&this->monotonicMs);
} else {
this->monotonicMs = 0;
firstScanDone = true;
} else if (Compat_clock_monotonic_gettime(&now) == 0) {
// save time in millisecond, so with a delay in deciseconds
// there are no irregularities
this->scanTs = 1000 * now.tv_sec + now.tv_nsec / 1000000;
}
ProcessList_goThroughEntries(this, false);
for (int i = Vector_size(this->processes) - 1; i >= 0; i--) {
Process* p = (Process*) Vector_get(this->processes, i);
if (p->tombTs > 0) {
if (p->tombStampMs > 0) {
// remove tombed process
if (this->scanTs >= p->tombTs) {
if (this->monotonicMs >= p->tombStampMs) {
ProcessList_remove(this, p);
}
} else if (p->updated == false) {
// process no longer exists
if (this->settings->highlightChanges && p->wasShown) {
// mark tombed
p->tombTs = this->scanTs + 1000 * this->settings->highlightDelaySecs;
p->tombStampMs = this->monotonicMs + 1000 * this->settings->highlightDelaySecs;
} else {
// immediately remove
ProcessList_remove(this, p);

View File

@ -48,8 +48,9 @@ typedef struct ProcessList_ {
Hashtable* displayTreeSet;
Hashtable* draftingTreeSet;
struct timeval timestamp; /* time of the current sample */
uint64_t timestampMs; /* current time in milliseconds */
struct timeval realtime; /* time of the current sample */
uint64_t realtimeMs; /* current time in milliseconds */
uint64_t monotonicMs; /* same, but from monotonic clock */
Panel* panel;
int following;
@ -79,8 +80,6 @@ typedef struct ProcessList_ {
memory_t cachedSwap;
unsigned int cpuCount;
time_t scanTs;
} ProcessList;
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId);

View File

@ -15,6 +15,7 @@ in the source distribution for its full text.
#include "CRT.h"
#include "FunctionBar.h"
#include "Object.h"
#include "Platform.h"
#include "ProcessList.h"
#include "ProvideCurses.h"
#include "XUtils.h"
@ -92,9 +93,8 @@ void ScreenManager_resize(ScreenManager* this, int x1, int y1, int x2, int y2) {
static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTimeout, bool* redraw, bool* rescan, bool* timedOut) {
ProcessList* pl = this->header->pl;
struct timeval tv;
gettimeofday(&tv, NULL);
double newTime = ((double)tv.tv_sec * 10) + ((double)tv.tv_usec / 100000);
Platform_gettime_realtime(&pl->realtime, &pl->realtimeMs);
double newTime = ((double)pl->realtime.tv_sec * 10) + ((double)pl->realtime.tv_usec / 100000);
*timedOut = (newTime - *oldTime > this->settings->delay);
*rescan |= *timedOut;

View File

@ -37,6 +37,10 @@ in the source distribution for its full text.
#include "zfs/ZfsArcMeter.h"
#include "zfs/ZfsCompressedArcMeter.h"
#ifdef HAVE_HOST_GET_CLOCK_SERVICE
#include <mach/clock.h>
#include <mach/mach.h>
#endif
#ifdef HAVE_MACH_MACH_TIME_H
#include <mach/mach_time.h>
#endif
@ -405,3 +409,23 @@ void Platform_getBattery(double* percent, ACPresence* isOnAC) {
CFRelease(list);
CFRelease(power_sources);
}
void Platform_gettime_monotonic(uint64_t* msec) {
#ifdef HAVE_HOST_GET_CLOCK_SERVICE
clock_serv_t cclock;
mach_timespec_t mts;
host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock);
clock_get_time(cclock, &mts);
mach_port_deallocate(mach_task_self(), cclock);
*msec = ((uint64_t)mts.tv_sec * 1000) + ((uint64_t)mts.tv_nsec / 1000000);
#else
Generic_gettime_monotomic(msec);
#endif
}

View File

@ -19,6 +19,7 @@ in the source distribution for its full text.
#include "NetworkIOMeter.h"
#include "ProcessLocksScreen.h"
#include "SignalsPanel.h"
#include "generic/gettime.h"
#include "generic/hostname.h"
#include "generic/uname.h"
@ -85,4 +86,10 @@ static inline bool Platform_getLongOption(ATTR_UNUSED int opt, ATTR_UNUSED int a
return false;
}
static inline void Platform_gettime_realtime(struct timeval* tv, uint64_t* msec) {
Generic_gettime_realtime(tv, msec);
}
void Platform_gettime_monotonic(uint64_t* msec);
#endif

View File

@ -17,6 +17,7 @@ in the source distribution for its full text.
#include "NetworkIOMeter.h"
#include "ProcessLocksScreen.h"
#include "SignalsPanel.h"
#include "generic/gettime.h"
#include "generic/hostname.h"
#include "generic/uname.h"
@ -75,4 +76,12 @@ static inline bool Platform_getLongOption(ATTR_UNUSED int opt, ATTR_UNUSED int a
return false;
}
static inline void Platform_gettime_realtime(struct timeval* tv, uint64_t* msec) {
Generic_gettime_realtime(tv, msec);
}
static inline void Platform_gettime_monotonic(uint64_t* msec) {
Generic_gettime_monotonic(msec);
}
#endif

View File

@ -18,6 +18,7 @@ in the source distribution for its full text.
#include "Process.h"
#include "ProcessLocksScreen.h"
#include "SignalsPanel.h"
#include "generic/gettime.h"
#include "generic/hostname.h"
#include "generic/uname.h"
@ -80,4 +81,12 @@ static inline bool Platform_getLongOption(ATTR_UNUSED int opt, ATTR_UNUSED int a
return false;
}
static inline void Platform_gettime_realtime(struct timeval* tv, uint64_t* msec) {
Generic_gettime_realtime(tv, msec);
}
static inline void Platform_gettime_monotonic(uint64_t* msec) {
Generic_gettime_monotonic(msec);
}
#endif

57
generic/gettime.c Normal file
View File

@ -0,0 +1,57 @@
/*
htop - generic/gettime.c
(C) 2021 htop dev team
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "config.h" // IWYU pragma: keep
#include <string.h>
#include <time.h>
#include "generic/gettime.h"
void Generic_gettime_realtime(struct timeval* tvp, uint64_t* msec) {
#if defined(HAVE_CLOCK_GETTIME)
struct timespec ts;
if (clock_gettime(CLOCK_REALTIME, &ts) == 0) {
tvp->tv_sec = ts.tv_sec;
tvp->tv_usec = ts.tv_nsec / 1000;
*msec = ((uint64_t)ts.tv_sec * 1000) + ((uint64_t)ts.tv_nsec / 1000000);
} else {
memset(tvp, 0, sizeof(struct timeval));
*msec = 0;
}
#else /* lower resolution gettimeofday(2) is always available */
struct timeval tv;
if (gettimeofday(&tv, NULL) == 0) {
*tsp = tv; /* struct copy */
*msec = ((uint64_t)tv.tv_sec * 1000) + ((uint64_t)tv.tv_usec / 1000);
} else {
memset(tvp, 0, sizeof(struct timeval));
*msec = 0;
}
#endif
}
void Generic_gettime_monotonic(uint64_t* msec) {
#if defined(HAVE_CLOCK_GETTIME)
struct timespec ts;
if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0)
*msec = ((uint64_t)ts.tv_sec * 1000) + ((uint64_t)ts.tv_nsec / 1000000);
else
*msec = 0;
#else
# error "No monotonic clock available"
#endif
}

19
generic/gettime.h Normal file
View File

@ -0,0 +1,19 @@
#ifndef HEADER_gettime
#define HEADER_gettime
/*
htop - generic/gettime.h
(C) 2021 htop dev team
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include <stdint.h>
#include <sys/time.h>
#include <sys/types.h>
void Generic_gettime_realtime(struct timeval* ts, uint64_t* msec);
void Generic_gettime_monotonic(uint64_t* msec);
#endif

View File

@ -189,13 +189,6 @@ static void LinuxProcessList_updateCPUcount(ProcessList* super, FILE* stream) {
}
}
static void LinuxProcessList_updateTime(LinuxProcessList* this) {
ProcessList* pl = &(this->super);
gettimeofday(&pl->timestamp, NULL);
pl->timestampMs = (uint64_t)&pl->timestamp.tv_sec * 1000 + (uint64_t)pl->timestamp.tv_usec / 1000;
}
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) {
LinuxProcessList* this = xCalloc(1, sizeof(LinuxProcessList));
ProcessList* pl = &(this->super);
@ -1975,7 +1968,6 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
LinuxProcessList* this = (LinuxProcessList*) super;
const Settings* settings = super->settings;
LinuxProcessList_updateTime(this);
LinuxProcessList_scanMemoryInfo(super);
LinuxProcessList_scanHugePages(this);
LinuxProcessList_scanZfsArcstats(this);
@ -2005,5 +1997,5 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
openat_arg_t rootFd = "";
#endif
LinuxProcessList_recurseProcTree(this, rootFd, PROCDIR, NULL, period, super->timestampMs);
LinuxProcessList_recurseProcTree(this, rootFd, PROCDIR, NULL, period, super->realtimeMs);
}

View File

@ -19,6 +19,7 @@ in the source distribution for its full text.
#include "Process.h"
#include "ProcessLocksScreen.h"
#include "SignalsPanel.h"
#include "generic/gettime.h"
#include "generic/hostname.h"
#include "generic/uname.h"
@ -93,4 +94,12 @@ void Platform_longOptionsUsage(const char* name);
bool Platform_getLongOption(int opt, int argc, char** argv);
static inline void Platform_gettime_realtime(struct timeval* tv, uint64_t* msec) {
Generic_gettime_realtime(tv, msec);
}
static inline void Platform_gettime_monotonic(uint64_t* msec) {
Generic_gettime_monotonic(msec);
}
#endif

View File

@ -19,6 +19,7 @@ in the source distribution for its full text.
#include "Process.h"
#include "ProcessLocksScreen.h"
#include "SignalsPanel.h"
#include "generic/gettime.h"
#include "generic/hostname.h"
#include "generic/uname.h"
@ -78,4 +79,12 @@ static inline bool Platform_getLongOption(ATTR_UNUSED int opt, ATTR_UNUSED int a
return false;
}
static inline void Platform_gettime_realtime(struct timeval* tv, uint64_t* msec) {
Generic_gettime_realtime(tv, msec);
}
static inline void Platform_gettime_monotonic(uint64_t* msec) {
Generic_gettime_monotonic(msec);
}
#endif

View File

@ -26,6 +26,7 @@ in the source distribution for its full text.
#include "ProcessLocksScreen.h"
#include "SignalsPanel.h"
#include "generic/gettime.h"
#include "generic/hostname.h"
#include "generic/uname.h"
@ -101,4 +102,12 @@ static inline bool Platform_getLongOption(ATTR_UNUSED int opt, ATTR_UNUSED int a
return false;
}
static inline void Platform_gettime_realtime(struct timeval* tv, uint64_t* msec) {
Generic_gettime_realtime(tv, msec);
}
static inline void Platform_gettime_monotonic(uint64_t* msec) {
Generic_gettime_monotonic(msec);
}
#endif

View File

@ -15,6 +15,7 @@ in the source distribution for its full text.
#include "ProcessLocksScreen.h"
#include "SignalsPanel.h"
#include "UnsupportedProcess.h"
#include "generic/gettime.h"
extern const SignalItem Platform_signals[];
@ -69,4 +70,12 @@ static inline bool Platform_getLongOption(ATTR_UNUSED int opt, ATTR_UNUSED int a
return false;
}
static inline void Platform_gettime_realtime(struct timeval* tv, uint64_t* msec) {
Generic_gettime_realtime(tv, msec);
}
static inline void Platform_gettime_monotonic(uint64_t* msec) {
Generic_gettime_monotonic(msec);
}
#endif