chiark / gitweb /
procfs-util: add APIs to get consumed CPU time and used memory from /proc
authorLennart Poettering <lennart@poettering.net>
Fri, 9 Feb 2018 16:32:26 +0000 (17:32 +0100)
committerSven Eden <yamakuzure@gmx.net>
Wed, 30 May 2018 05:59:09 +0000 (07:59 +0200)
This is preparation for emulating the "usage_usec" keyed attribute of
the "cpu.stat" property of the root cgroup from data in /proc. Similar,
for emulating the "memory.current" attribute.

src/basic/procfs-util.c
src/basic/procfs-util.h
src/test/test-procfs-util.c

index ca2ec988ea371a43fdb9d8c953302db0cecc7d70..497db458711f17d56da2847c8c283a1321997a35 100644 (file)
@@ -3,6 +3,8 @@
 //#include <errno.h>
 
 //#include "alloc-util.h"
+//#include "def.h"
+//#include "fd-util.h"
 //#include "fileio.h"
 //#include "parse-util.h"
 //#include "process-util.h"
@@ -136,3 +138,131 @@ int procfs_tasks_get_current(uint64_t *ret) {
 
         return safe_atou64(nr, ret);
 }
+
+static uint64_t calc_gcd64(uint64_t a, uint64_t b) {
+
+        while (b > 0) {
+                uint64_t t;
+
+                t = a % b;
+
+                a = b;
+                b = t;
+        }
+
+        return a;
+}
+
+int procfs_cpu_get_usage(nsec_t *ret) {
+        _cleanup_free_ char *first_line = NULL;
+        unsigned long user_ticks = 0, nice_ticks = 0, system_ticks = 0,
+                irq_ticks = 0, softirq_ticks = 0,
+                guest_ticks = 0, guest_nice_ticks = 0;
+        long ticks_per_second;
+        uint64_t sum, gcd, a, b;
+        const char *p;
+        int r;
+
+        assert(ret);
+
+        r = read_one_line_file("/proc/stat", &first_line);
+        if (r < 0)
+                return r;
+
+        p = first_word(first_line, "cpu");
+        if (!p)
+                return -EINVAL;
+
+        if (sscanf(p, "%lu %lu %lu %*u %*u %lu %lu %*u %lu %lu",
+                   &user_ticks,
+                   &nice_ticks,
+                   &system_ticks,
+                   &irq_ticks,
+                   &softirq_ticks,
+                   &guest_ticks,
+                   &guest_nice_ticks) < 5) /* we only insist on the first five fields */
+                return -EINVAL;
+
+        ticks_per_second = sysconf(_SC_CLK_TCK);
+        if (ticks_per_second  < 0)
+                return -errno;
+        assert(ticks_per_second > 0);
+
+        sum = (uint64_t) user_ticks + (uint64_t) nice_ticks + (uint64_t) system_ticks +
+                (uint64_t) irq_ticks + (uint64_t) softirq_ticks +
+                (uint64_t) guest_ticks + (uint64_t) guest_nice_ticks;
+
+        /* Let's reduce this fraction before we apply it to avoid overflows when converting this to µsec */
+        gcd = calc_gcd64(NSEC_PER_SEC, ticks_per_second);
+
+        a = (uint64_t) NSEC_PER_SEC / gcd;
+        b = (uint64_t) ticks_per_second / gcd;
+
+        *ret = DIV_ROUND_UP((nsec_t) sum * (nsec_t) a, (nsec_t) b);
+        return 0;
+}
+
+int procfs_memory_get_current(uint64_t *ret) {
+        uint64_t mem_total = UINT64_MAX, mem_free = UINT64_MAX;
+        _cleanup_fclose_ FILE *f = NULL;
+        int r;
+
+        assert(ret);
+
+        f = fopen("/proc/meminfo", "re");
+        if (!f)
+                return -errno;
+
+        for (;;) {
+                _cleanup_free_ char *line = NULL;
+                uint64_t *v;
+                char *p, *e;
+                size_t n;
+
+                r = read_line(f, LONG_LINE_MAX, &line);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        return -EINVAL; /* EOF: Couldn't find one or both fields? */
+
+                p = first_word(line, "MemTotal:");
+                if (p)
+                        v = &mem_total;
+                else {
+                        p = first_word(line, "MemFree:");
+                        if (p)
+                                v = &mem_free;
+                        else
+                                continue;
+                }
+
+                /* Determine length of numeric value */
+                n = strspn(p, DIGITS);
+                if (n == 0)
+                        return -EINVAL;
+                e = p + n;
+
+                /* Ensure the line ends in " kB" */
+                n = strspn(e, WHITESPACE);
+                if (n == 0)
+                        return -EINVAL;
+                if (!streq(e + n, "kB"))
+                        return -EINVAL;
+
+                *e = 0;
+                r = safe_atou64(p, v);
+                if (r < 0)
+                        return r;
+                if (*v == UINT64_MAX)
+                        return -EINVAL;
+
+                if (mem_total != UINT64_MAX && mem_free != UINT64_MAX)
+                        break;
+        }
+
+        if (mem_free > mem_total)
+                return -EINVAL;
+
+        *ret = (mem_total - mem_free) * 1024U;
+        return 0;
+}
index a03891e78cb55a77913d5cbdd44780c3db38ca75..757e79c0d7ccf576fff995b945f58b33a18cc420 100644 (file)
@@ -3,6 +3,12 @@
 
 //#include <inttypes.h>
 
+//#include "time-util.h"
+
 int procfs_tasks_get_limit(uint64_t *ret);
 int procfs_tasks_set_limit(uint64_t limit);
 int procfs_tasks_get_current(uint64_t *ret);
+
+int procfs_cpu_get_usage(nsec_t *ret);
+
+int procfs_memory_get_current(uint64_t *ret);
index 6c4d15ec8ac425864206d3438abf89a2c585bcb4..433b06b0542ba1a2b629ade78912346c65e801be 100644 (file)
@@ -3,15 +3,24 @@
 //#include <errno.h>
 
 //#include "log.h"
+//#include "parse-util.h"
 //#include "procfs-util.h"
 
 int main(int argc, char *argv[]) {
+        char buf[CONST_MAX(FORMAT_TIMESPAN_MAX, FORMAT_BYTES_MAX)];
+        nsec_t nsec;
         uint64_t v;
         int r;
 
         log_parse_environment();
         log_open();
 
+        assert_se(procfs_cpu_get_usage(&nsec) >= 0);
+        log_info("Current sytem CPU time: %s", format_timespan(buf, sizeof(buf), nsec/NSEC_PER_USEC, 1));
+
+        assert_se(procfs_memory_get_current(&v) >= 0);
+        log_info("Current memory usage: %s", format_bytes(buf, sizeof(buf), v));
+
         assert_se(procfs_tasks_get_current(&v) >= 0);
         log_info("Current number of tasks: %" PRIu64, v);