[lxc-devel] [lxcfs/master] bindings: calculate uptime via proc/<pid>/stat

brauner on Github lxc-bot at linuxcontainers.org
Fri May 26 03:42:17 UTC 2017


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 395 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20170526/2c155a50/attachment.bin>
-------------- next part --------------
From 17bc44b8a4ade4fcd16ae5b9b4aced8fdbab761b Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Fri, 26 May 2017 05:06:30 +0200
Subject: [PATCH] bindings: calculate uptime via proc/<pid>/stat

Closes #165.
Closes #184.

Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
 bindings.c | 204 +++++++++++++++++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 177 insertions(+), 27 deletions(-)

diff --git a/bindings.c b/bindings.c
index 5b5860e..05adb26 100644
--- a/bindings.c
+++ b/bindings.c
@@ -8,14 +8,17 @@
 
 #define FUSE_USE_VERSION 26
 
+#define __STDC_FORMAT_MACROS
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <fuse.h>
+#include <inttypes.h>
 #include <libgen.h>
 #include <pthread.h>
 #include <sched.h>
 #include <stdbool.h>
+#include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -30,11 +33,15 @@
 #include <sys/param.h>
 #include <sys/socket.h>
 #include <sys/syscall.h>
+#include <sys/sysinfo.h>
 #include <sys/vfs.h>
 
 #include "bindings.h"
 #include "config.h" // for VERSION
 
+/* Maximum number for 64 bit integer is a string with 21 digits: 2^64 - 1 = 21 */
+#define LXCFS_NUMSTRLEN64 21
+
 /* Define pivot_root() if missing from the C library */
 #ifndef HAVE_PIVOT_ROOT
 static int pivot_root(const char * new_root, const char * put_old)
@@ -3454,25 +3461,167 @@ static int proc_cpuinfo_read(char *buf, size_t size, off_t offset,
 	return rv;
 }
 
-static long int getreaperctime(pid_t pid)
+static uint64_t get_reaper_start_time(pid_t pid)
 {
-	char fnam[100];
-	struct stat sb;
 	int ret;
+	FILE *f;
+	uint64_t starttime;
+	/* strlen("/proc/") = 6
+	 * +
+	 * LXCFS_NUMSTRLEN
+	 * +
+	 * strlen("/stat") = 5
+	 * +
+	 * \0 = 1
+	 * */
+	char path[6 + LXCFS_NUMSTRLEN64 + 5 + 1];
 	pid_t qpid;
 
 	qpid = lookup_initpid_in_store(pid);
-	if (qpid <= 0)
+	if (qpid <= 0) {
+		/* Caller can check for EINVAL on 0. */
+		errno = EINVAL;
+		return 0;
+	}
+
+	ret = snprintf(path, MAXPATHLEN, "/proc/%d/stat", qpid);
+	if (ret < 0 || ret >= MAXPATHLEN) {
+		/* Caller can check for EINVAL on 0. */
+		errno = EINVAL;
+		return 0;
+	}
+
+	f = fopen(path, "r");
+	if (!f) {
+		/* Caller can check for EINVAL on 0. */
+		errno = EINVAL;
+		return 0;
+	}
+
+	/* Note that the *scanf() argument supression requires that length
+	 * modifiers such as "l" are omitted. Otherwise some compilers will yell
+	 * at us. It's like telling someone you're not married and then asking
+	 * if you can bring your wife to the party.
+	 */
+	ret = fscanf(f, "%*d "      /* (1)  pid         %d   */
+			"%*s "      /* (2)  comm        %s   */
+			"%*c "      /* (3)  state       %c   */
+			"%*d "      /* (4)  ppid        %d   */
+			"%*d "      /* (5)  pgrp        %d   */
+			"%*d "      /* (6)  session     %d   */
+			"%*d "      /* (7)  tty_nr      %d   */
+			"%*d "      /* (8)  tpgid       %d   */
+			"%*u "      /* (9)  flags       %u   */
+			"%*u "      /* (10) minflt      %lu  */
+			"%*u "      /* (11) cminflt     %lu  */
+			"%*u "      /* (12) majflt      %lu  */
+			"%*u "      /* (13) cmajflt     %lu  */
+			"%*u "      /* (14) utime       %lu  */
+			"%*u "      /* (15) stime       %lu  */
+			"%*d "      /* (16) cutime      %ld  */
+			"%*d "      /* (17) cstime      %ld  */
+			"%*d "      /* (18) priority    %ld  */
+			"%*d "      /* (19) nice        %ld  */
+			"%*d "      /* (20) num_threads %ld  */
+			"%*d "      /* (21) itrealvalue %ld  */
+			"%" PRIu64, /* (22) starttime   %llu */
+		     &starttime);
+	if (ret != 1) {
+		/* Caller can check for EINVAL on 0. */
+		errno = EINVAL;
 		return 0;
+	}
 
-	ret = snprintf(fnam, 100, "/proc/%d", qpid);
-	if (ret < 0 || ret >= 100)
+	fclose(f);
+
+	errno = 0;
+	return starttime;
+}
+
+static double get_reaper_start_time_in_sec(pid_t pid)
+{
+	uint64_t clockticks;
+	int64_t ticks_per_sec;
+	double uptime;
+
+	clockticks = get_reaper_start_time(pid);
+	if (clockticks == 0 && errno == EINVAL) {
+		lxcfs_debug("failed to retrieve start time of pid %d\n", pid);
 		return 0;
+	}
+
+	ticks_per_sec = sysconf(_SC_CLK_TCK);
+	if (ticks_per_sec < 0 && errno == EINVAL) {
+		lxcfs_debug("%s\n", "failed to determine number of clock ticks in a second");
+		return 0;
+	}
 
-	if (lstat(fnam, &sb) < 0)
+	uptime = clockticks;
+	uptime /= ticks_per_sec;
+
+	return uptime;
+}
+
+static uint64_t get_rounded_reaper_start_time(pid_t pid)
+{
+	double uptime;
+
+	/* This is a very basic rounding routine. Let's not get crazy and link
+	 * against math.h.
+	 */
+	uptime = get_reaper_start_time_in_sec(pid);
+	if (uptime < 0.0)
+		return (uint64_t)(uptime - 0.5);
+
+	return (uint64_t)(uptime + 0.5);
+}
+
+static double get_reaper_age(pid_t pid)
+{
+	uint64_t procstart;
+	double uptime, procage;
+
+	/* We need to substract the time the process has started since system
+	 * boot minus the time when the system has started to get the actual
+	 * reaper age.
+	 */
+	procstart = get_rounded_reaper_start_time(pid);
+	procage = procstart;
+	if (procstart > 0) {
+		int ret;
+		struct timespec spec;
+
+		ret = clock_gettime(CLOCK_BOOTTIME, &spec);
+		if (ret < 0)
+			return 0;
+		/* We could make this more precise here by using the tv_nsec
+		 * field in the timespec struct and convert it to milliseconds
+		 * and then create a double for the seconds and milliseconds but
+		 * that seems more work than it is worth.
+		 */
+		uptime = spec.tv_sec;
+		procage = uptime - procstart;
+	}
+
+	return procage;
+}
+
+static uint64_t get_reaper_btime(pid)
+{
+	int ret;
+	struct sysinfo sys;
+	uint64_t procstart;
+	uint64_t uptime;
+
+	ret = sysinfo(&sys);
+	if (ret < 0) {
+		lxcfs_debug("%s\n", "failed to retrieve system information");
 		return 0;
+	}
 
-	return sb.st_ctime;
+	uptime = (uint64_t)time(NULL) - (uint64_t)sys.uptime;
+	procstart = get_rounded_reaper_start_time(pid);
+	return uptime - procstart;
 }
 
 #define CPUALL_MAX_SIZE (BUF_RESERVE_SIZE / 2)
@@ -3539,7 +3688,7 @@ static int proc_stat_read(char *buf, size_t size, off_t offset,
 		if (sscanf(line, "cpu%9[^ ]", cpu_char) != 1) {
 			/* not a ^cpuN line containing a number N, just print it */
 			if (strncmp(line, "btime", 5) == 0)
-				l = snprintf(cache, cache_size, "btime %ld\n", getreaperctime(fc->pid));
+				l = snprintf(cache, cache_size, "btime %"PRIu64"\n", get_reaper_btime(fc->pid));
 			else
 				l = snprintf(cache, cache_size, "%s", line);
 			if (l < 0) {
@@ -3649,16 +3798,13 @@ static int proc_stat_read(char *buf, size_t size, off_t offset,
 	return rv;
 }
 
-static long int getreaperage(pid_t pid)
-{
-	long int ctime;
-
-	ctime = getreaperctime(pid);
-	if (ctime)
-		return time(NULL) - ctime;
-	return ctime;
-}
-
+/* This function retrieves the busy time of a group of tasks by looking at
+ * cpuacct.usage. Unfortunately, this only makes sense when the container has
+ * been given it's own cpuacct cgroup. If not, this function will take the busy
+ * time of all other taks that do not actually belong to the container into
+ * account as well. If someone has a clever solution for this please send a
+ * patch!
+ */
 static unsigned long get_reaper_busy(pid_t task)
 {
 	pid_t initpid = lookup_initpid_in_store(task);
@@ -3704,10 +3850,10 @@ static int proc_uptime_read(char *buf, size_t size, off_t offset,
 {
 	struct fuse_context *fc = fuse_get_context();
 	struct file_info *d = (struct file_info *)fi->fh;
-	long int reaperage = getreaperage(fc->pid);
-	unsigned long int busytime = get_reaper_busy(fc->pid), idletime;
+	unsigned long int busytime = get_reaper_busy(fc->pid);
 	char *cache = d->buf;
 	ssize_t total_len = 0;
+	double idletime, reaperage;
 
 #if RELOADTEST
 	iwashere();
@@ -3724,13 +3870,17 @@ static int proc_uptime_read(char *buf, size_t size, off_t offset,
 		return total_len;
 	}
 
-	idletime = reaperage - busytime;
-	if (idletime > reaperage)
-		idletime = reaperage;
+	reaperage = get_reaper_age(fc->pid);
+	/* To understand why this is done, please read the comment to the
+	 * get_reaper_busy() function.
+	 */
+	idletime = reaperage;
+	if (reaperage >= busytime)
+		idletime = reaperage - busytime;
 
-	total_len = snprintf(d->buf, d->size, "%ld.0 %lu.0\n", reaperage, idletime);
-	if (total_len < 0){
-		perror("Error writing to cache");
+	total_len = snprintf(d->buf, d->size, "%.2f %.2f\n", reaperage, idletime);
+	if (total_len < 0 || total_len >=  d->size){
+		lxcfs_error("%s\n", "failed to write to cache");
 		return 0;
 	}
 


More information about the lxc-devel mailing list