diff options
author | Anita Zhang <the.anitazha@gmail.com> | 2020-03-07 17:58:33 -0800 |
---|---|---|
committer | Anita Zhang <the.anitazha@gmail.com> | 2020-10-07 16:17:24 -0700 |
commit | 42b83e8e3324e2dc876c537aef2345d1939f10a1 (patch) | |
tree | ccaae5c92d7154094d3e44a01b698e7ab0bf5c6e | |
parent | 510ca79cf2443bba5089f34e1fec2466cf5312ac (diff) | |
download | systemd-42b83e8e3324e2dc876c537aef2345d1939f10a1.tar.gz |
shared: helpers to read pressure stats from cgroups
-rw-r--r-- | src/shared/meson.build | 2 | ||||
-rw-r--r-- | src/shared/psi-util.c | 118 | ||||
-rw-r--r-- | src/shared/psi-util.h | 30 | ||||
-rw-r--r-- | src/test/meson.build | 4 | ||||
-rw-r--r-- | src/test/test-psi-util.c | 80 |
5 files changed, 234 insertions, 0 deletions
diff --git a/src/shared/meson.build b/src/shared/meson.build index 723e00e437..9ef12959e5 100644 --- a/src/shared/meson.build +++ b/src/shared/meson.build @@ -187,6 +187,8 @@ shared_sources = files(''' pkcs11-util.h pretty-print.c pretty-print.h + psi-util.c + psi-util.h ptyfwd.c ptyfwd.h pwquality-util.c diff --git a/src/shared/psi-util.c b/src/shared/psi-util.c new file mode 100644 index 0000000000..21e965b04b --- /dev/null +++ b/src/shared/psi-util.c @@ -0,0 +1,118 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include <stdio.h> +#include <unistd.h> + +#include "alloc-util.h" +#include "extract-word.h" +#include "fd-util.h" +#include "fileio.h" +#include "parse-util.h" +#include "psi-util.h" +#include "string-util.h" +#include "stat-util.h" +#include "strv.h" + +int read_resource_pressure(const char *path, PressureType type, ResourcePressure *ret) { + _cleanup_free_ char *line = NULL; + _cleanup_fclose_ FILE *f = NULL; + unsigned field_filled = 0; + ResourcePressure rp = {}; + const char *t, *cline; + char *word; + int r; + + assert(path); + assert(IN_SET(type, PRESSURE_TYPE_SOME, PRESSURE_TYPE_FULL)); + assert(ret); + + if (type == PRESSURE_TYPE_SOME) + t = "some"; + else if (type == PRESSURE_TYPE_FULL) + t = "full"; + else + return -EINVAL; + + r = fopen_unlocked(path, "re", &f); + if (r < 0) + return r; + + for (;;) { + _cleanup_free_ char *l = NULL; + char *w; + + r = read_line(f, LONG_LINE_MAX, &l); + if (r < 0) + return r; + if (r == 0) + break; + + w = first_word(l, t); + if (w) { + line = TAKE_PTR(l); + cline = w; + break; + } + } + + if (!line) + return -ENODATA; + + /* extracts either avgX=Y.Z or total=X */ + while ((r = extract_first_word(&cline, &word, NULL, 0)) > 0) { + _cleanup_free_ char *w = word; + const char *v; + + if ((v = startswith(w, "avg10="))) { + if (field_filled & (1U << 0)) + return -EINVAL; + + field_filled |= 1U << 0; + r = parse_loadavg_fixed_point(v, &rp.avg10); + } else if ((v = startswith(w, "avg60="))) { + if (field_filled & (1U << 1)) + return -EINVAL; + + field_filled |= 1U << 1; + r = parse_loadavg_fixed_point(v, &rp.avg60); + } else if ((v = startswith(w, "avg300="))) { + if (field_filled & (1U << 2)) + return -EINVAL; + + field_filled |= 1U << 2; + r = parse_loadavg_fixed_point(v, &rp.avg300); + } else if ((v = startswith(w, "total="))) { + if (field_filled & (1U << 3)) + return -EINVAL; + + field_filled |= 1U << 3; + r = safe_atou64(v, &rp.total); + } else + continue; + + if (r < 0) + return r; + } + + if (r < 0) + return r; + + if (field_filled != 15U) + return -EINVAL; + + *ret = rp; + return 0; +} + +int is_pressure_supported(void) { + const char *p; + + FOREACH_STRING(p, "/proc/pressure/cpu", "/proc/pressure/io", "/proc/pressure/memory") + if (access(p, F_OK) < 0) { + if (errno == ENOENT) + return 0; + return -errno; + } + + return 1; +} diff --git a/src/shared/psi-util.h b/src/shared/psi-util.h new file mode 100644 index 0000000000..9810dbec6e --- /dev/null +++ b/src/shared/psi-util.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include <stdbool.h> + +#include "parse-util.h" +#include "time-util.h" + +typedef enum PressureType { + PRESSURE_TYPE_SOME, + PRESSURE_TYPE_FULL, +} PressureType; + +/* Averages are stored in fixed-point with 11 bit fractions */ +typedef struct ResourcePressure { + loadavg_t avg10; + loadavg_t avg60; + loadavg_t avg300; + usec_t total; +} ResourcePressure; + +/** Upstream 4.20+ format + * + * some avg10=0.22 avg60=0.17 avg300=1.11 total=58761459 + * full avg10=0.23 avg60=0.16 avg300=1.08 total=58464525 + */ +int read_resource_pressure(const char *path, PressureType type, ResourcePressure *ret); + +/* Was the kernel compiled with CONFIG_PSI=y? 1 if yes, 0 if not, negative on error. */ +int is_pressure_supported(void); diff --git a/src/test/meson.build b/src/test/meson.build index 9bb3499963..60dec9512c 100644 --- a/src/test/meson.build +++ b/src/test/meson.build @@ -804,6 +804,10 @@ tests += [ [['src/test/test-local-addresses.c'], [], []], + + [['src/test/test-psi-util.c'], + [], + []], ] ############################################################ diff --git a/src/test/test-psi-util.c b/src/test/test-psi-util.c new file mode 100644 index 0000000000..bde8ef80b1 --- /dev/null +++ b/src/test/test-psi-util.c @@ -0,0 +1,80 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include <linux/loadavg.h> + +#include "alloc-util.h" +#include "fileio.h" +#include "fs-util.h" +#include "psi-util.h" +#include "tests.h" + +static void test_read_mem_pressure(void) { + _cleanup_(unlink_tempfilep) char path[] = "/tmp/pressurereadtestXXXXXX"; + ResourcePressure rp; + + if (geteuid() != 0) + return (void) log_tests_skipped("not root"); + + assert_se(mkstemp(path)); + + assert_se(read_resource_pressure("/verylikelynonexistentpath", PRESSURE_TYPE_SOME, &rp) < 0); + assert_se(read_resource_pressure(path, PRESSURE_TYPE_SOME, &rp) < 0); + + assert_se(write_string_file(path, "herpdederp\n", WRITE_STRING_FILE_CREATE) == 0); + assert_se(read_resource_pressure(path, PRESSURE_TYPE_SOME, &rp) < 0); + + /* Pressure file with some invalid values*/ + assert_se(write_string_file(path, "some avg10=0.22=55 avg60=0.17=8 avg300=1.11=00 total=58761459\n" + "full avg10=0.23=55 avg60=0.16=8 avg300=1.08=00 total=58464525", WRITE_STRING_FILE_CREATE) == 0); + assert_se(read_resource_pressure(path, PRESSURE_TYPE_SOME, &rp) < 0); + + /* Same pressure valid values as below but with duplicate avg60 field */ + assert_se(write_string_file(path, "some avg10=0.22 avg60=0.17 avg60=0.18 avg300=1.11 total=58761459\n" + "full avg10=0.23 avg60=0.16 avg300=1.08 total=58464525", WRITE_STRING_FILE_CREATE) == 0); + assert_se(read_resource_pressure(path, PRESSURE_TYPE_SOME, &rp) < 0); + + assert_se(write_string_file(path, "some avg10=0.22 avg60=0.17 avg300=1.11 total=58761459\n" + "full avg10=0.23 avg60=0.16 avg300=1.08 total=58464525", WRITE_STRING_FILE_CREATE) == 0); + assert_se(read_resource_pressure(path, PRESSURE_TYPE_SOME, &rp) == 0); + assert_se(LOAD_INT(rp.avg10) == 0); + assert_se(LOAD_FRAC(rp.avg10) == 22); + assert_se(LOAD_INT(rp.avg60) == 0); + assert_se(LOAD_FRAC(rp.avg60) == 17); + assert_se(LOAD_INT(rp.avg300) == 1); + assert_se(LOAD_FRAC(rp.avg300) == 11); + assert_se(rp.total == 58761459); + assert(read_resource_pressure(path, PRESSURE_TYPE_FULL, &rp) == 0); + assert_se(LOAD_INT(rp.avg10) == 0); + assert_se(LOAD_FRAC(rp.avg10) == 23); + assert_se(LOAD_INT(rp.avg60) == 0); + assert_se(LOAD_FRAC(rp.avg60) == 16); + assert_se(LOAD_INT(rp.avg300) == 1); + assert_se(LOAD_FRAC(rp.avg300) == 8); + assert_se(rp.total == 58464525); + + /* Pressure file with extra unsupported fields */ + assert_se(write_string_file(path, "some avg5=0.55 avg10=0.22 avg60=0.17 avg300=1.11 total=58761459\n" + "full avg10=0.23 avg60=0.16 avg300=1.08 avg600=2.00 total=58464525", WRITE_STRING_FILE_CREATE) == 0); + assert_se(read_resource_pressure(path, PRESSURE_TYPE_SOME, &rp) == 0); + assert_se(LOAD_INT(rp.avg10) == 0); + assert_se(LOAD_FRAC(rp.avg10) == 22); + assert_se(LOAD_INT(rp.avg60) == 0); + assert_se(LOAD_FRAC(rp.avg60) == 17); + assert_se(LOAD_INT(rp.avg300) == 1); + assert_se(LOAD_FRAC(rp.avg300) == 11); + assert_se(rp.total == 58761459); + assert(read_resource_pressure(path, PRESSURE_TYPE_FULL, &rp) == 0); + assert_se(LOAD_INT(rp.avg10) == 0); + assert_se(LOAD_FRAC(rp.avg10) == 23); + assert_se(LOAD_INT(rp.avg60) == 0); + assert_se(LOAD_FRAC(rp.avg60) == 16); + assert_se(LOAD_INT(rp.avg300) == 1); + assert_se(LOAD_FRAC(rp.avg300) == 8); + assert_se(rp.total == 58464525); +} + +int main(void) { + test_setup_logging(LOG_DEBUG); + test_read_mem_pressure(); + return 0; +} |