Remove calls to time(2) in process_diskstat

This patch addresses inaccuracies in timekeeping in the Zabbix
disk metrics for Linux and FreeBSD, replacing the call to time
(and all rate calculations performed with it) with sub-second
resolution timing.

It also slightly reduces the overheads in executing this code
by moving the current time evaluation to a callsite outside of
the zabbix_agent collector thread loop iterating over all disk
devices.

Signed-off-by: Nathan Scott <nathans@redhat.com>


Index: libs/zbxsysinfo/linux/diskio.c
===================================================================
--- libs/zbxsysinfo/linux/diskio.c	(revision 34305)
+++ libs/zbxsysinfo/linux/diskio.c	(working copy)
@@ -239,7 +239,7 @@
 		if (SUCCEED != get_diskstat(kernel_devname, dstats))
 			return SYSINFO_RET_FAIL;
 
-		if (NULL == (device = collector_diskdevice_add(kernel_devname)))
+		if (NULL == (device = collector_diskdevice_add(kernel_devname, zbx_time())))
 			return SYSINFO_RET_FAIL;
 	}
 
Index: libs/zbxsysinfo/freebsd/diskio.c
===================================================================
--- libs/zbxsysinfo/freebsd/diskio.c	(revision 34305)
+++ libs/zbxsysinfo/freebsd/diskio.c	(working copy)
@@ -176,7 +176,7 @@
 		if (FAIL == get_diskstat(pd, dstats))	/* validate device name */
 			return SYSINFO_RET_FAIL;
 
-		if (NULL == (device = collector_diskdevice_add(pd)))
+		if (NULL == (device = collector_diskdevice_add(pd, zbx_time())))
 			return SYSINFO_RET_FAIL;
 	}
 
Index: zabbix_agent/stats.c
===================================================================
--- zabbix_agent/stats.c	(revision 34305)
+++ zabbix_agent/stats.c	(working copy)
@@ -413,6 +413,8 @@
  ******************************************************************************/
 ZBX_THREAD_ENTRY(collector_thread, args)
 {
+	double		timestamp;
+
 	assert(args);
 
 	zabbix_log(LOG_LEVEL_WARNING, "agent #%d started [collector]", ((zbx_thread_args_t *)args)->thread_num);
@@ -425,6 +427,7 @@
 	while (ZBX_IS_RUNNING())
 	{
 		zbx_setproctitle("collector [processing data]");
+		timestamp = zbx_time();
 #ifdef _WINDOWS
 		collect_perfstat();
 #else
@@ -432,7 +435,7 @@
 			collect_cpustat(&(collector->cpus));
 
 		if (0 != DISKDEVICE_COLLECTOR_STARTED(collector))
-			collect_stats_diskdevices();
+			collect_stats_diskdevices(timestamp);
 #endif
 #ifdef _AIX
 		if (1 == collector->vmstat.enabled)
Index: zabbix_agent/diskdevices.h
===================================================================
--- zabbix_agent/diskdevices.h	(revision 34305)
+++ zabbix_agent/diskdevices.h	(working copy)
@@ -32,7 +32,7 @@
 {
 	char		name[32];
 	int		index;
-	time_t		clock[MAX_COLLECTOR_HISTORY];
+	double		clock[MAX_COLLECTOR_HISTORY];
 	zbx_uint64_t	r_sect[MAX_COLLECTOR_HISTORY];
 	zbx_uint64_t	r_oper[MAX_COLLECTOR_HISTORY];
 	zbx_uint64_t	r_byte[MAX_COLLECTOR_HISTORY];
@@ -57,7 +57,7 @@
 #define DISKDEVICE_COLLECTOR_STARTED(collector)	((collector) && (collector)->diskstat_shmid != NONEXISTENT_SHMID)
 
 ZBX_SINGLE_DISKDEVICE_DATA	*collector_diskdevice_get(const char *devname);
-ZBX_SINGLE_DISKDEVICE_DATA	*collector_diskdevice_add(const char *devname);
-void				collect_stats_diskdevices();
+ZBX_SINGLE_DISKDEVICE_DATA	*collector_diskdevice_add(const char *devname, double timestamp);
+void				collect_stats_diskdevices(double timestamp);
 
 #endif
Index: zabbix_agent/diskdevices.c
===================================================================
--- zabbix_agent/diskdevices.c	(revision 34305)
+++ zabbix_agent/diskdevices.c	(working copy)
@@ -29,10 +29,10 @@
 #define LOCK_DISKSTATS		zbx_mutex_lock(&diskstats_lock)
 #define UNLOCK_DISKSTATS	zbx_mutex_unlock(&diskstats_lock)
 
-static void	apply_diskstat(ZBX_SINGLE_DISKDEVICE_DATA *device, time_t now, zbx_uint64_t *dstat)
+static void	apply_diskstat(ZBX_SINGLE_DISKDEVICE_DATA *device, double timestamp, zbx_uint64_t *dstat)
 {
 	register int	i;
-	time_t		clock[ZBX_AVG_COUNT], sec;
+	double		clock[ZBX_AVG_COUNT], sec;
 	int		index[ZBX_AVG_COUNT];
 
 	assert(device);
@@ -42,7 +42,7 @@
 	if (MAX_COLLECTOR_HISTORY == device->index)
 		device->index = 0;
 
-	device->clock[device->index] = now;
+	device->clock[device->index] = timestamp;
 	device->r_sect[device->index] = dstat[ZBX_DSTAT_R_SECT];
 	device->r_oper[device->index] = dstat[ZBX_DSTAT_R_OPER];
 	device->r_byte[device->index] = dstat[ZBX_DSTAT_R_BYTE];
@@ -50,16 +50,16 @@
 	device->w_oper[device->index] = dstat[ZBX_DSTAT_W_OPER];
 	device->w_byte[device->index] = dstat[ZBX_DSTAT_W_BYTE];
 
-	clock[ZBX_AVG1] = clock[ZBX_AVG5] = clock[ZBX_AVG15] = now + 1;
+	clock[ZBX_AVG1] = clock[ZBX_AVG5] = clock[ZBX_AVG15] = timestamp + 1.0;
 	index[ZBX_AVG1] = index[ZBX_AVG5] = index[ZBX_AVG15] = -1;
 
 	for (i = 0; i < MAX_COLLECTOR_HISTORY; i++)
 	{
-		if (0 == device->clock[i])
+		if (0.0 == device->clock[i])
 			continue;
 
 #define DISKSTAT(t)\
-		if ((device->clock[i] >= (now - (t * 60))) && (clock[ZBX_AVG ## t] > device->clock[i]))\
+		if ((device->clock[i] >= (timestamp - (t * 60))) && (clock[ZBX_AVG ## t] > device->clock[i]))\
 		{\
 			clock[ZBX_AVG ## t] = device->clock[i];\
 			index[ZBX_AVG ## t] = i;\
@@ -71,24 +71,24 @@
 	}
 
 #define SAVE_DISKSTAT(t)\
-	if (-1 == index[ZBX_AVG ## t] || 0 == now - device->clock[index[ZBX_AVG ## t]])\
+	if (-1 == index[ZBX_AVG ## t] || 0.0 == timestamp - device->clock[index[ZBX_AVG ## t]])\
 	{\
-		device->r_sps[ZBX_AVG ## t] = 0;\
-		device->r_ops[ZBX_AVG ## t] = 0;\
-		device->r_bps[ZBX_AVG ## t] = 0;\
-		device->w_sps[ZBX_AVG ## t] = 0;\
-		device->w_ops[ZBX_AVG ## t] = 0;\
-		device->w_bps[ZBX_AVG ## t] = 0;\
+		device->r_sps[ZBX_AVG ## t] = 0.0;\
+		device->r_ops[ZBX_AVG ## t] = 0.0;\
+		device->r_bps[ZBX_AVG ## t] = 0.0;\
+		device->w_sps[ZBX_AVG ## t] = 0.0;\
+		device->w_ops[ZBX_AVG ## t] = 0.0;\
+		device->w_bps[ZBX_AVG ## t] = 0.0;\
 	}\
 	else\
 	{\
-		sec = now - device->clock[index[ZBX_AVG ## t]];\
-		device->r_sps[ZBX_AVG ## t] = (dstat[ZBX_DSTAT_R_SECT] - device->r_sect[index[ZBX_AVG ## t]]) / (double)sec;\
-		device->r_ops[ZBX_AVG ## t] = (dstat[ZBX_DSTAT_R_OPER] - device->r_oper[index[ZBX_AVG ## t]]) / (double)sec;\
-		device->r_bps[ZBX_AVG ## t] = (dstat[ZBX_DSTAT_R_BYTE] - device->r_byte[index[ZBX_AVG ## t]]) / (double)sec;\
-		device->w_sps[ZBX_AVG ## t] = (dstat[ZBX_DSTAT_W_SECT] - device->w_sect[index[ZBX_AVG ## t]]) / (double)sec;\
-		device->w_ops[ZBX_AVG ## t] = (dstat[ZBX_DSTAT_W_OPER] - device->w_oper[index[ZBX_AVG ## t]]) / (double)sec;\
-		device->w_bps[ZBX_AVG ## t] = (dstat[ZBX_DSTAT_W_BYTE] - device->w_byte[index[ZBX_AVG ## t]]) / (double)sec;\
+		sec = timestamp - device->clock[index[ZBX_AVG ## t]];\
+		device->r_sps[ZBX_AVG ## t] = (dstat[ZBX_DSTAT_R_SECT] - device->r_sect[index[ZBX_AVG ## t]]) / sec;\
+		device->r_ops[ZBX_AVG ## t] = (dstat[ZBX_DSTAT_R_OPER] - device->r_oper[index[ZBX_AVG ## t]]) / sec;\
+		device->r_bps[ZBX_AVG ## t] = (dstat[ZBX_DSTAT_R_BYTE] - device->r_byte[index[ZBX_AVG ## t]]) / sec;\
+		device->w_sps[ZBX_AVG ## t] = (dstat[ZBX_DSTAT_W_SECT] - device->w_sect[index[ZBX_AVG ## t]]) / sec;\
+		device->w_ops[ZBX_AVG ## t] = (dstat[ZBX_DSTAT_W_OPER] - device->w_oper[index[ZBX_AVG ## t]]) / sec;\
+		device->w_bps[ZBX_AVG ## t] = (dstat[ZBX_DSTAT_W_BYTE] - device->w_byte[index[ZBX_AVG ## t]]) / sec;\
 	}
 
 	SAVE_DISKSTAT(1);
@@ -96,19 +96,17 @@
 	SAVE_DISKSTAT(15);
 }
 
-static void	process_diskstat(ZBX_SINGLE_DISKDEVICE_DATA *device)
+static void	process_diskstat(ZBX_SINGLE_DISKDEVICE_DATA *device, double timestamp)
 {
-	time_t		now;
 	zbx_uint64_t	dstat[ZBX_DSTAT_MAX];
 
-	now = time(NULL);
 	if (FAIL == get_diskstat(device->name, dstat))
 		return;
 
-	apply_diskstat(device, now, dstat);
+	apply_diskstat(device, timestamp, dstat);
 }
 
-void	collect_stats_diskdevices()
+void	collect_stats_diskdevices(double timestamp)
 {
 	int	i;
 
@@ -116,7 +114,7 @@
 	diskstat_shm_reattach();
 
 	for (i = 0; i < diskdevices->count; i++)
-		process_diskstat(&diskdevices->device[i]);
+		process_diskstat(&diskdevices->device[i], timestamp);
 
 	UNLOCK_DISKSTATS;
 }
@@ -153,7 +151,7 @@
 	return device;
 }
 
-ZBX_SINGLE_DISKDEVICE_DATA	*collector_diskdevice_add(const char *devname)
+ZBX_SINGLE_DISKDEVICE_DATA	*collector_diskdevice_add(const char *devname, double timestamp)
 {
 	const char			*__function_name = "collector_diskdevice_add";
 	ZBX_SINGLE_DISKDEVICE_DATA	*device = NULL;
@@ -182,7 +180,7 @@
 	device->index = -1;
 	(diskdevices->count)++;
 
-	process_diskstat(device);
+	process_diskstat(device, timestamp);
 end:
 	UNLOCK_DISKSTATS;
 
