procfs-util: add APIs to get consumed CPU time and used memory from /proc
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.
This commit is contained in:
parent
ba4b1544f2
commit
a04fcf17ba
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in New Issue